Verso una metrica di fluidità dell'animazione

Scopri come misurare le animazioni, come considerare i frame dell'animazione e l'uniformità generale della pagina.

Behdad Bakhshinategh
Behdad Bakhshinategh
Jonathan Ross
Jonathan Ross
Michal Mocny
Michal Mocny

Probabilmente hai notato che le pagine si "interrompono" o si "bloccano" durante lo scorrimento o le animazioni. Ci piace dire che queste esperienze non sono semplici. Per risolvere questi tipi di problemi, il team di Chrome sta lavorando per aggiungere ulteriore supporto ai nostri strumenti di lab per il rilevamento delle animazioni, oltre ad apportare miglioramenti costanti alla diagnostica della pipeline di rendering in Chromium.

Vorremmo condividere alcuni progressi recenti, offrirti indicazioni concrete sugli strumenti e discutere delle idee per le future metriche relative alla fluidità delle animazioni. Come sempre, ci piacerebbe ricevere il tuo feedback.

Questo post tratterà tre argomenti principali:

  • Un rapido sguardo alle animazioni e ai relativi frame.
  • Ecco le nostre attuali opinioni sulla misurazione dell'uniformità generale delle animazioni.
  • Ecco alcuni suggerimenti pratici che potrai mettere in pratica oggi stesso negli strumenti da laboratorio.

Che cosa sono le animazioni?

Le animazioni danno vita ai contenuti. Spostando i contenuti, soprattutto in risposta alle interazioni degli utenti, le animazioni possono rendere un'esperienza più naturale, comprensibile e divertente.

Tuttavia, le animazioni implementate male o la semplice aggiunta di troppe animazioni possono deteriorare l'esperienza e renderla decisamente per niente divertente. Probabilmente abbiamo interagito tutti con un'interfaccia in cui sono stati aggiunti troppi effetti di transizione "utili", che in realtà diventano ostili da sperimentare quando hanno prestazioni scarse. Di conseguenza, alcuni utenti potrebbero preferire movimento ridotto, una preferenza dell'utente che dovresti rispettare.

Come funzionano le animazioni?

Per riassumere, la pipeline di rendering è costituita da alcune fasi sequenziali:

  1. Stile: calcola gli stili applicabili agli elementi.
  2. Layout: genera la geometria e la posizione di ogni elemento.
  3. Paint: compila i pixel di ogni elemento nei livelli.
  4. Composito: disegna i livelli sullo schermo.

Esistono molti modi per definire le animazioni, ma fondamentalmente tutti funzionano tramite uno dei seguenti:

  • Modifica delle proprietà di layout.
  • Modifica delle proprietà di paint.
  • Modifica delle proprietà composite.

Poiché queste fasi sono sequenziali, è importante definire le animazioni in termini di proprietà che si trovano più in basso nella pipeline. Quanto prima l'aggiornamento avviene nella procedura, tanto maggiori sono i costi e ci sono minori probabilità che sia uniforme. Per ulteriori dettagli, consulta Prestazioni di rendering.

Sebbene possa essere pratico animare le proprietà del layout, questo passaggio comporta dei costi, anche se non sono immediatamente evidenti. Le animazioni devono essere definite in termini di modifiche di proprietà composte, ove possibile.

La definizione di animazioni CSS dichiarative o l'utilizzo di animazioni web e la possibilità di animare proprietà composite è un ottimo primo passo per garantire animazioni fluide ed efficienti. Tuttavia, questo da solo non garantisce l'uniformità, perché anche le animazioni web efficienti hanno limiti di prestazioni. Ecco perché è sempre importante misurare i dati.

Che cosa sono i frame dell'animazione?

La visualizzazione degli aggiornamenti alla rappresentazione visiva di una pagina richiede del tempo. Una modifica visiva porterà a un nuovo frame dell'animazione, che verrà infine visualizzato sul display dell'utente.

Visualizza gli aggiornamenti a intervalli regolari, quindi gli aggiornamenti visivi vengono raggruppati. Molti display vengono aggiornati a un intervallo di tempo fisso, ad esempio 60 volte al secondo (ossia 60 Hz). Alcuni display più moderni possono offrire frequenze di aggiornamento più elevate (90-120 Hz stanno diventando comuni). Spesso questi display possono adattarsi attivamente tra le frequenze di aggiornamento secondo necessità o addirittura offrire frequenze fotogrammi completamente variabili.

L'obiettivo di qualsiasi applicazione, come un gioco o un browser, è elaborare tutti questi aggiornamenti visivi in gruppo e produrre ogni volta un frame di animazione visivamente completo entro la scadenza. Tieni presente che questo obiettivo è completamente diverso da altre attività importanti del browser, come il caricamento rapido dei contenuti dalla rete o l'esecuzione efficiente di attività JavaScript.

A un certo punto, può diventare troppo difficile completare tutti gli aggiornamenti visivi entro la scadenza assegnata assegnata dal display. In questo caso, il browser abbandona un frame. Lo schermo non diventa nero, ma si ripete. L'aggiornamento visivo è stato eseguito per un po' più di tempo: lo stesso frame dell'animazione che è stato presentato all'opportunità di frame precedente.

Questo accade spesso! Non è necessariamente percepibile, soprattutto per i contenuti statici o simili a documenti, che sono comuni a una piattaforma web in particolare. I fotogrammi eliminati diventano evidenti solo quando si verificano importanti aggiornamenti visivi, come le animazioni, per le quali abbiamo bisogno di un flusso costante di aggiornamenti delle animazioni per mostrare un movimento fluido.

Cosa influisce sui frame delle animazioni?

Gli sviluppatori web possono avere un impatto enorme sulla capacità di un browser di visualizzare e presentare aggiornamenti visivi in modo rapido ed efficiente.

Ecco alcuni esempi:

  • Utilizzo di contenuti troppo grandi o che consumano molte risorse per decodificare rapidamente sul dispositivo di destinazione.
  • L'utilizzo di troppi livelli richiede troppa memoria GPU.
  • La definizione di animazioni web o stili CSS troppo complessi.
  • Utilizzo di anti-pattern di progettazione che disattivano le ottimizzazioni del rendering rapide.
  • Troppo lavoro JS sul thread principale, portando a lunghe attività che bloccano gli aggiornamenti visivi.

Ma come fare a sapere se un fotogramma di un'animazione ha scaduto la scadenza causando un calo?

Un metodo possibile è utilizzare i sondaggi di requestAnimationFrame(), ma presenta diversi svantaggi. requestAnimationFrame(), o "rAF", indica al browser che vuoi eseguire un'animazione e chiede un'opportunità di farlo prima della fase di colorazione successiva della pipeline di rendering. Se la funzione di callback non viene chiamata nel momento previsto, significa che non è stata eseguita la colorazione e che uno o più frame sono stati ignorati. Mediante il polling e il conteggio della frequenza di chiamata rAF, puoi calcolare una sorta di metrica "frame al secondo" (FPS).

let frameTimes = [];
function pollFramesPerSecond(now) {
  frameTimes = [...frameTimes.filter(t => t > now - 1000), now];
  requestAnimationFrame(pollFramesPerSecond);
  console.log('Frames per second:', frameTimes.length);
}
requestAnimationFrame(pollFramesPerSecond);

L'utilizzo del sondaggio requestAnimationFrame() non è una buona idea per diversi motivi:

  • Ogni script deve impostare il proprio ciclo di polling.
  • Possono bloccare il percorso critico.
  • Anche se il polling rAF è veloce, può impedire a requestIdleCallback() di essere in grado di programmare blocchi inattivi per lunghi periodi se utilizzato continuamente (blocchi che superano un singolo frame).
  • Analogamente, la mancanza di blocchi inattivi da molto tempo impedisce al browser di pianificare altre attività a lunga esecuzione (come la garbage collection più lunga e altre operazioni in background o speculative).
  • Se il polling è attivato e disattivato, non perderai i casi in cui il budget del frame è stato superato.
  • Il polling segnalerà falsi positivi nei casi in cui il browser utilizzi una frequenza di aggiornamento variabile (ad esempio a causa dello stato di alimentazione o visibilità).
  • E, soprattutto, non cattura tutti i tipi di aggiornamenti delle animazioni.

Troppo lavoro sul thread principale può influire sulla possibilità di vedere i frame dell'animazione. Dai un'occhiata al esempio di Jank per vedere come un'animazione basata su rAF, quando c'è troppo lavoro sul thread principale (come il layout), causerà la perdita di frame, meno callback rAF e un numero di FPS inferiore.

Quando il thread principale si blocca, gli aggiornamenti visivi iniziano a interrompersi. È jank!

Molti strumenti di misurazione si sono concentrati molto sulla capacità del thread principale di restituire in modo tempestivo e dei frame di animazione senza intoppi. Ma non è tutta la storia. Considera l'esempio seguente:

Il video riportato sopra mostra una pagina che inserisce periodicamente attività lunghe nel thread principale. Queste attività lunghe rovinano completamente la capacità della pagina di fornire determinati tipi di aggiornamenti visivi e puoi notare nell'angolo in alto a sinistra un calo corrispondente di requestAnimationFrame() FPS segnalati a 0.

Nonostante queste attività lunghe, la pagina continua a scorrere senza problemi. Questo perché, nei browser moderni, lo scorrimento è spesso in thread, gestito interamente dal compositor.

Questo è un esempio che contiene contemporaneamente molti frame esclusi nel thread principale, ma che ha ancora molti frame di scorrimento pubblicati correttamente nel thread composito. Una volta completata l'attività lunga, l'aggiornamento della visualizzazione del thread principale non comporta comunque alcuna modifica visiva. Il polling rAF ha suggerito di impostare un frame a 0, ma visivamente un utente non sarebbe stato in grado di notare alcuna differenza.

Per i frame dell'animazione, la storia non è così semplice.

Frame dell'animazione: aggiornamenti importanti

L'esempio riportato sopra mostra che la notizia non è solo requestAnimationFrame().

Quindi, quando sono importanti gli aggiornamenti e i frame dell'animazione? Ecco alcuni criteri su cui stiamo valutando e su cui vorremmo ricevere un feedback:

  • Aggiornamenti dei thread principali e del compositore
  • Aggiornamenti di colorazione mancanti
  • Rilevamento delle animazioni
  • Qualità e quantità

Aggiornamenti dei thread principali e del compositore

Gli aggiornamenti dei frame dell'animazione non sono booleani. Non è vero che i frame possono essere completamente eliminati o presentati completamente. Esistono molti motivi per cui un frame di animazione può essere parzialmente presentato. In altre parole, può avere contemporaneamente contenuti obsoleti e anche alcuni nuovi aggiornamenti visivi.

L'esempio più comune è quando il browser non è in grado di produrre un nuovo aggiornamento del thread principale entro la scadenza del frame, ma ha un nuovo aggiornamento del thread del compositore (come l'esempio dello scorrimento in thread di cui sopra).

Un motivo importante per cui si consiglia di utilizzare animazioni dichiarative per animare proprietà composte è che consente di gestire completamente un'animazione dal thread del compositore anche quando il thread principale è occupato. Questi tipi di animazioni possono continuare a produrre aggiornamenti visivi in modo efficiente e in parallelo.

Invece, potrebbero verificarsi casi in cui un aggiornamento del thread principale diventa finalmente disponibile per la presentazione, ma solo dopo aver perso diverse scadenze dei frame. In questo caso il browser avrà alcuni nuovi aggiornamenti, che però potrebbero non essere le più recenti.

In generale, consideriamo i frame che contengono alcuni nuovi aggiornamenti visivi, senza tutti i nuovi aggiornamenti visivi, come frame parziale. I frame parziali sono piuttosto comuni. Idealmente, gli aggiornamenti parziali includerebbero almeno gli aggiornamenti visivi più importanti, come le animazioni, ma ciò può accadere solo se le animazioni sono basate sul thread del compositore.

Aggiornamenti di colorazione mancanti

Un altro tipo di aggiornamento parziale si verifica quando i contenuti multimediali come le immagini non hanno terminato la decodifica e la rasterizzazione nel tempo necessario per la presentazione del frame.

Oppure, anche se una pagina è perfettamente statica, i browser potrebbero comunque restare indietro nel rendering degli aggiornamenti visivi durante lo scorrimento rapido. Questo perché le versioni in pixel dei contenuti oltre l'area visibile potrebbero essere ignorate per risparmiare memoria GPU. La visualizzazione dei pixel richiede un po' di tempo e potrebbe impiegare più tempo di un singolo frame per visualizzare i contenuti dopo uno scorrimento di grandi dimensioni, ad esempio un tocco con un dito. Questo è comunemente noto come checkboarding.

Con ogni opportunità di rendering dei frame, è possibile monitorare quanti degli ultimi aggiornamenti visivi sono stati effettivamente visualizzati sullo schermo. La misurazione della capacità di eseguire questa operazione su più frame (o nel tempo) è comunemente nota come velocità effettiva frame.

Se la GPU è davvero bloccata, il browser (o la piattaforma) potrebbe persino iniziare a limitare la frequenza con cui prova gli aggiornamenti visivi, riducendo così la frequenza fotogrammi effettiva. Anche se tecnicamente può ridurre il numero di aggiornamenti frame persi, visivamente apparirà comunque con una velocità effettiva frame inferiore.

Tuttavia, non tutti i tipi di velocità effettiva di frame bassa sono negativi. Se la pagina è per lo più inattiva e non ci sono animazioni attive, una frequenza fotogrammi bassa è visivamente accattivante quanto una frequenza fotogrammi elevata (e può far risparmiare batteria).

Quindi quando è importante la velocità effettiva dei frame?

Rilevamento delle animazioni

La velocità effettiva dei frame elevata è importante soprattutto nei periodi in cui sono presenti animazioni importanti. I diversi tipi di animazione dipendono dagli aggiornamenti visivi provenienti da un thread specifico (principale, compositor o worker), pertanto l'aggiornamento visivo dipende dal thread che fornisce l'aggiornamento entro la scadenza. Diciamo che un determinato thread influisce sull'uniformità ogni volta che è presente un'animazione attiva che dipende dall'aggiornamento del thread.

Alcuni tipi di animazioni sono più facili da definire e rilevare rispetto ad altri. Le animazioni dichiarative, o quelle basate sull'input dell'utente, sono più chiare da definire rispetto alle animazioni basate su JavaScript implementate come aggiornamenti periodici delle proprietà di stile animabili.

Anche con requestAnimationFrame() non puoi sempre presumere che ogni chiamata rAF produca necessariamente un aggiornamento visivo o un'animazione. Ad esempio, l'utilizzo del polling rAF solo per monitorare la frequenza fotogrammi (come mostrato sopra) non dovrebbe influire sulle misurazioni della fluidità poiché non è disponibile alcun aggiornamento visivo.

Qualità e quantità

Infine, il rilevamento delle animazioni e degli aggiornamenti dei frame dell'animazione è ancora solo una parte della storia, poiché acquisisce solo la quantità di aggiornamenti delle animazioni e non la qualità.

Ad esempio, mentre guardi un video, potresti ottenere una frequenza fotogrammi costante di 60 f/s. Tecnicamente, l'operazione è perfettamente fluida, ma il video stesso potrebbe avere una bassa velocità in bit o problemi di buffering di rete. Ciò non viene acquisito direttamente dalle metriche di fluidità dell'animazione, ma potrebbe comunque essere sconvolgente per l'utente.

Oppure, un gioco che sfrutta <canvas> (ad esempio usando tecniche come canvas offscreen per garantire una frequenza fotogrammi stabile) tecnicamente potrebbe essere perfettamente fluido in termini di fotogrammi animati, senza riuscire a caricare nella scena risorse di gioco di alta qualità o non mostrare artefatti di rendering.

E ovviamente un sito può avere solo alcune animazioni davvero scadenti 🙂

GIF vecchia scuola in costruzione

Voglio dire, immagino che siano stati fantastici per il loro tempo!

Stati di un singolo frame dell'animazione

Poiché i frame potrebbero essere presentati parzialmente o possono verificarsi in modi che non influiscono sulla fluidità, abbiamo iniziato a considerare ogni frame come un punteggio di completezza o fluidità.

Ecco lo spettro di modi in cui interpretiamo lo stato di un singolo frame dell'animazione, ordinato dal migliore al peggiore.

Nessun aggiornamento desiderato Tempo di inattività, ripetizione del frame precedente.
Presentazione completa Il commit dell'aggiornamento del thread principale è stato eseguito entro la scadenza oppure non è stato richiesto alcun aggiornamento del thread principale.
Presentazione parziale Solo compositore; l'aggiornamento ritardato del thread principale non ha comportato modifiche visive.
Presentazione parziale Solo composito. Il thread principale aveva un aggiornamento visivo, che però non includeva un'animazione che influisce sull'uniformità.
Presentazione parziale Solo composito. Il thread principale aveva un aggiornamento visivo che influisce sull'uniformità, ma è stato utilizzato un frame precedentemente inattivo.
Presentazione parziale Solo il compositore, senza l'aggiornamento principale desiderato e l'aggiornamento del compositore ha un'animazione che influisce sull'uniformità.
Presentazione parziale Solo il compositore, ma l'aggiornamento del compositore non ha un'animazione che influisce sulla fluidità.
Frame ignorato Nessun aggiornamento. Non è stato richiesto alcun aggiornamento del compositore e l'elemento principale è stato ritardato.
Frame ignorato Volevi un aggiornamento del compositore, che però è stato ritardato.
Cornice inattiva Era necessario un aggiornamento, che è stato prodotto dal renderer, ma la GPU non lo ha ancora presentato prima della scadenza vsync.

È possibile trasformare questi stati in un punteggio. E forse un modo per interpretare questo punteggio è considerarlo una probabilità di essere osservabile da parte dell'utente. Un singolo frame ignorato potrebbe non essere molto osservabile, ma sicuramente c'è una sequenza di molti frame ignorati che influiscono sull'uniformità di una riga.

Riepilogo: la metrica Percentuale frame ignorati

A volte può essere necessario approfondire lo stato di ogni frame dell'animazione, ma è anche utile assegnare un punteggio di riepilogo rapido per un'esperienza.

Dal momento che i frame possono essere parzialmente presentati e dato che anche gli aggiornamenti dei frame completamente ignorati potrebbero non influire sull'uniformità, vogliamo concentrarci meno sul solo conteggio dei frame e più sull'estensione per cui il browser non è in grado di fornire aggiornamenti visivamente completi quando è importante.

Il modello mentale dovrebbe passare da:

  1. Frame al secondo, a
  2. Rilevare aggiornamenti mancanti e importanti delle animazioni, per
  3. Percentuale diminuita in un determinato periodo di tempo.

Ciò che conta è: la quantità di tempo in attesa di aggiornamenti importanti. Riteniamo che ciò corrisponda al modo naturale in cui gli utenti sperimentano la fluidità dei contenuti web. Finora abbiamo utilizzato le seguenti metriche come set iniziale di metriche:

  • Percentuale media ignorata: per tutti i frame di animazione non inattivi nell'intera sequenza temporale
  • Caso peggiore in percentuale di frame ignorati: misurati in finestre di tempo scorrevoli di 1 secondo.
  • 95° percentile di frame eliminati in percentuale: misurato in finestre di tempo scorrevoli di 1 secondo.

Oggi puoi trovare questi punteggi in alcuni strumenti per sviluppatori di Chrome. Sebbene queste metriche si concentrino solo sulla velocità effettiva complessiva del frame, stiamo valutando anche altri fattori, come la latenza dei frame.

Fai una prova con gli strumenti per sviluppatori.

HUD prestazioni

In Chromium è presente un pratico HUD per le prestazioni nascosto dietro un flag (chrome://flags/#show-performance-metrics-hud). Puoi trovare i punteggi in tempo reale relativi ad esempio a Segnali web essenziali e alcune definizioni sperimentali per l'uniformità delle animazioni in base alla Percentuale di frame eliminati nel tempo.

HUD prestazioni

Statistiche di rendering del frame

Abilita "Statistiche di rendering frame" in DevTools tramite le impostazioni di rendering per vedere una visione in diretta dei nuovi frame dell'animazione, con codifica per colore per distinguere gli aggiornamenti parziali dagli aggiornamenti dei frame completamente eliminati. Il valore FPS indicato si riferisce solo ai frame completamente presentati.

Statistiche di rendering del frame

Visualizzatore frame nelle registrazioni del profilo delle prestazioni DevTools

Il riquadro Rendimento di DevTools ha da tempo un visualizzatore di frame. Tuttavia, era un po' fuori sincronia con il funzionamento della moderna pipeline di rendering. Di recente sono stati apportati molti miglioramenti, anche nell'ultima versione di Chrome Canary, che riteniamo faciliteranno notevolmente il debug dei problemi relativi all'animazione.

Oggi scoprirai che i frame nel visualizzatore dei frame sono meglio allineati con i limiti di vsync e sono codificati per colore in base allo stato. Non c'è ancora una visualizzazione completa per tutte le sfumature descritte sopra, ma abbiamo in programma di aggiungerne altre nel prossimo futuro.

Visualizzatore frame in Chrome DevTools

Tracciamento in Chrome

Infine, con il tracciamento di Chrome, lo strumento preferito per approfondire i dettagli, puoi registrare una traccia "Rendering dei contenuti web" tramite la nuova UI perfetta (o about:tracing) e approfondire la pipeline delle immagini di Chrome. Può essere un'attività scoraggiante, ma di recente sono state aggiunte alcune funzioni a Chromium per semplificarlo. Puoi ottenere una panoramica di ciò che è disponibile nel documento Life of a Frame.

Tramite gli eventi di traccia puoi determinare in modo definitivo:

  • Quali animazioni sono in esecuzione (con eventi denominati TrackerValidation).
  • Recupero della sequenza temporale esatta dei frame dell'animazione (utilizzando gli eventi denominati PipelineReporter).
  • Per gli aggiornamenti di un'animazione di scarsa qualità, scopri esattamente cosa blocca l'esecuzione più rapida dell'animazione (utilizzando le suddivisioni degli eventi all'interno degli eventi PipelineReporter).
  • Per le animazioni basate sull'input, scopri quanto tempo occorre per ricevere un aggiornamento visivo (utilizzando gli eventi denominati EventLatency).

Reporter della pipeline di tracciamento di Chrome

Passaggi successivi

L'iniziativa Web Vitals mira a fornire metriche e indicazioni per creare esperienze utente ottimali sul web. Le metriche basate su lab come il Tempo di blocco totale (TBT) sono fondamentali per rilevare e diagnosticare potenziali problemi di interattività. Abbiamo in programma di progettare una metrica simile basata su lab per assicurare l'uniformità delle animazioni.

Ti aggiorneremo man mano che continuiamo a sviluppare idee per la progettazione di una metrica completa basata sui dati dei singoli frame dell'animazione.

In futuro, vorremmo anche progettare API che rendano possibile la misurazione efficiente dell'uniformità dell'animazione per utenti reali nel campo e in lab. Continua a seguirci per altri aggiornamenti.

Feedback

Siamo entusiasti di tutti i recenti miglioramenti e degli strumenti per sviluppatori forniti in Chrome per misurare l'uniformità delle animazioni. Prova questi strumenti, controlla le animazioni e indicaci i risultati.

Puoi inviare i tuoi commenti al gruppo Google web-vitals-feedback con "[Metriche di fluidità]" nella riga dell'oggetto. Non vediamo l'ora di sapere cosa ne pensi.