Evita vernici inutili

Paul Lewis

Introduzione

La colorazione degli elementi di un sito o di un'applicazione può diventare molto costosa e può avere un effetto negativo sulle nostre prestazioni di runtime. In questo articolo diamo una rapida occhiata a cosa può attivare la colorazione nel browser e come puoi evitare che si verifichi un colore non necessario.

Pittura: un tour superveloce

Una delle attività principali che un browser deve eseguire è la conversione del DOM e del CSS in pixel sullo schermo tramite una procedura abbastanza complessa. Inizia leggendo il markup e da qui viene creato un albero DOM. L'operazione esegue un'operazione simile con il CSS e da qui viene creato il CSSOM. Il DOM e il CSSOM vengono quindi combinati e alla fine si giunge a una struttura da cui possiamo iniziare a disegnare alcuni pixel.

Il processo di pittura in sé è interessante. In Chrome, l'albero combinato di DOM e CSS viene rasterizzato da un software chiamato Skia. Se hai mai giocato con l'API di Skia dell'elemento canvas, ti sembrerebbe terribilmente familiare; ci sono molte funzioni in stile moveTo e lineTo oltre a molte altre più avanzate. Essenzialmente, tutti gli elementi che devono essere dipinti vengono distillati in una raccolta di chiamate Skia eseguibili e l'output è costituito da un gruppo di bitmap. Questi bitmap vengono caricati nella GPU, che la aiuta a comporre le immagini per darci l'immagine finale sullo schermo.

Da Dom a pixel

La cosa più importante è che il carico di lavoro di Skia dipende direttamente dagli stili che applichi ai tuoi elementi. Se utilizzi stili che utilizzano un algoritmo intensivo, Skia avrà molto lavoro da fare. Colt McAnlis ha scritto un articolo su come CSS influisce sul peso del rendering della pagina, quindi ti consigliamo di leggerlo per maggiori dettagli.

Detto questo, l'esecuzione della verniciatura richiede tempo e, se non lo riduciamo, supereremo il budget del frame di circa 16 ms. Gli utenti noteranno la perdita di frame e lo vedranno come jank, il che influisce negativamente sull'esperienza utente della nostra app. Questo non vogliamo davvero, quindi vediamo quali elementi causano la necessità di colorare e cosa possiamo fare.

Scorrimento

Ogni volta che scorri verso l'alto o verso il basso nel browser, è necessario ridipingere i contenuti prima che vengano visualizzati sullo schermo. Tutto bene sarà solo una piccola area, ma anche in questo caso, agli elementi che devono essere disegnati potrebbero essere applicati stili complessi. Quindi, anche se hai una piccola area da dipingere, non significa che il processo avverrà rapidamente.

Per vedere quali aree vengono ridipinte, puoi utilizzare la funzionalità "Mostra rettangoli di pittura" in DevTools di Chrome (basta fare clic sull'icona a forma di ingranaggio nell'angolo in basso a destra). Poi, con DevTools aperto, interagisci con la pagina e vedrai dei rettangoli lampeggianti che mostrano dove e quando Chrome ha dipinto una parte della pagina.

Mostra rettangoli Paint in Chrome DevTools
Mostra rettangoli Paint in Chrome DevTools

Le prestazioni dello scorrimento sono fondamentali per il successo del sito; gli utenti notano davvero quando il sito o l'applicazione non scorre bene e non gli piacciono. Di conseguenza, è nostro interesse far sì che la verniciatura rimanga accesa durante le pergamene in modo che gli utenti non vedano jank.

Ho già scritto un articolo sul rendimento dello scorrimento: dai un'occhiata a questo se vuoi saperne di più sulle specifiche riguardanti il rendimento dello scorrimento.

Interazioni

Le interazioni sono un'altra causa del lavoro di colorazione: passi del mouse, clic, tocchi, trascinazioni. Ogni volta che l'utente esegue una di queste interazioni, ad esempio con un passaggio del mouse, Chrome deve colorare di nuovo l'elemento interessato. Come accade per lo scorrimento, se è necessaria una colorazione estesa e complessa noterai un calo della frequenza fotogrammi.

Tutti vogliono animazioni piacevoli, fluide e interattive, quindi ancora una volta dovremo verificare se gli stili che cambiano nell'animazione ci costano troppo tempo.

Una combinazione sfortunata

Una demo con vernici costose
Una demo con vernici costose

Che cosa succede se scorro il mouse e mi capita di spostare il mouse contemporaneamente? Mi è perfettamente possibile "interagire inavvertitamente con un elemento mentre lo scorro oltre, attivando una vernice costosa. Questo, a sua volta, potrebbe spingermi attraverso il mio budget di frame di circa 16,7 ms (il tempo necessario per rimanere al di sotto per raggiungere 60 frame al secondo). Ho creato una demo per mostrarti esattamente cosa intendo. Magari mentre scorri e muovi il mouse, vedrai attivati gli effetti al passaggio del mouse, ma vediamo in che modo gli strumenti DevTools di Chrome:

DevTools di Chrome che mostra frame costosi
Strumenti DevTools di Chrome che mostrano frame costosi

Nell'immagine sopra puoi vedere che DevTools registra il funzionamento di colorazione quando passo il mouse su uno dei blocchi. Nella mia demo ho usato degli stili super pesante per sottolineare il concetto, quindi di tanto in tanto effettuo il budget per il frame. L'ultima cosa che vorrei è dover fare questo lavoro di verniciatura inutilmente, soprattutto durante un rotolo quando c'è altro lavoro da fare.

Come possiamo evitare che accada? In realtà la correzione è piuttosto semplice da implementare. Il trucco è collegare un gestore scroll che disattiverà gli effetti del passaggio del mouse e imposterà un timer per attivarli di nuovo. Ciò significa che quando scorri il dito non dovrai eseguire costose colorazioni per l'interazione. Quando ti fermi per un tempo sufficiente, riteniamo che sia sicuro riattivarli.

Ecco il codice:

// Used to track the enabling of hover effects
var enableTimer = 0;

/*
 * Listen for a scroll and use that to remove
 * the possibility of hover effects
 */
window.addEventListener('scroll', function() {
  clearTimeout(enableTimer);
  removeHoverClass();

  // enable after 1 second, choose your own value here!
  enableTimer = setTimeout(addHoverClass, 1000);
}, false);

/**
 * Removes the hover class from the body. Hover styles
 * are reliant on this class being present
 */
function removeHoverClass() {
  document.body.classList.remove('hover');
}

/**
 * Adds the hover class to the body. Hover styles
 * are reliant on this class being present
 */
function addHoverClass() {
  document.body.classList.add('hover');
}

Come puoi vedere, utilizziamo una classe sul corpo per controllare se gli effetti del passaggio del mouse sono "consentiti" o meno e gli stili sottostanti si basano su questa classe per essere presenti:

/* Expect the hover class to be on the body
 before doing any hover effects */
.hover .block:hover {
 …
}

Questo è tutto.

Conclusione

Le prestazioni di rendering sono fondamentali per gli utenti che apprezzano la tua applicazione e dovresti sempre cercare di mantenere il carico di lavoro di colorazione al di sotto dei 16 ms. A questo scopo, ti consigliamo di eseguire l'integrazione tramite DevTools durante tutto il processo di sviluppo per identificare e risolvere i colli di bottiglia non appena si presentano.

Le interazioni involontarie, in particolare con elementi ad alta intensità di colorazione, possono essere molto costose e possono compromettere le prestazioni del rendering. Come hai visto, possiamo utilizzare una piccola porzione di codice per risolvere il problema.

Dai un'occhiata ai tuoi siti e alle tue applicazioni: potrebbero usare un po' di protezione della vernice?