Scopri di più sulla misurazione delle animazioni, su come considerare i frame delle animazioni e sulla fluidità complessiva della pagina.
Probabilmente ti sarà capitato di vedere pagine che "balbettano" o "si bloccano" durante lo scorrimento o le animazioni. Ci piace dire che queste esperienze non sono ottimali. Per risolvere questi tipi di problemi, il team di Chrome ha lavorato per aggiungere maggiore supporto ai nostri strumenti di laboratorio per il rilevamento delle animazioni, oltre a apportare miglioramenti costanti alla diagnostica della pipeline di rendering in Chromium.
Vorremmo condividere alcuni progressi recenti, offrire indicazioni concrete sugli strumenti e discutere idee per future metriche di fluidità dell'animazione. Come sempre, ci piacerebbe conoscere il tuo feedback.
Questo post tratterà tre argomenti principali:
- Una rapida occhiata alle animazioni e ai relativi frame.
- Il nostro attuale pensiero sulla misurazione della fluidità complessiva dell'animazione.
- Alcuni suggerimenti pratici da utilizzare oggi negli strumenti del lab.
Che cosa sono le animazioni?
Le animazioni danno vita ai contenuti. Se i contenuti si muovono, soprattutto in risposta alle interazioni degli utenti, le animazioni possono rendere un'esperienza più naturale, comprensibile e divertente.
Tuttavia, le animazioni implementate in modo errato o l'aggiunta di troppe animazioni possono peggiorare l'esperienza e renderla tutt'altro che divertente. Probabilmente tutti abbiamo interagito con un'interfaccia che ha aggiunto troppi effetti di transizione "utili", che in realtà diventano ostili all'esperienza quando funzionano male. Pertanto, alcuni utenti potrebbero preferire il movimento ridotto, una preferenza utente che dovresti rispettare.
Come funzionano le animazioni?
Per riassumere, la pipeline di rendering è costituita da alcune fasi sequenziali:
- Stile:calcola gli stili che si applicano agli elementi.
- Layout: genera la geometria e la posizione di ogni elemento.
- Paint:riempi i pixel di ogni elemento nei livelli.
- Composito: disegna i livelli sullo schermo.
Esistono molti modi per definire le animazioni, ma tutti funzionano fondamentalmente tramite uno dei seguenti metodi:
- Modifica delle proprietà del layout.
- Modifica delle proprietà di pittura.
- Modifica delle proprietà composite.
Poiché queste fasi sono sequenziali, è importante definire le animazioni in termini di proprietà che si trovano più avanti nella pipeline. Prima avviene l'aggiornamento nel processo, maggiori sono i costi e meno è probabile che venga eseguito senza problemi. Per maggiori dettagli, consulta Prestazioni di rendering.
Sebbene l'animazione delle proprietà di layout possa essere comoda, comporta dei costi, anche se non sono immediatamente evidenti. Le animazioni devono essere definite in termini di modifiche alle proprietà composite, ove possibile.
Definire animazioni CSS dichiarative o utilizzare Web Animations, e assicurarsi di animare le proprietà composite, è un ottimo primo passo per garantire animazioni fluide ed efficienti. Tuttavia, questo da solo non garantisce la fluidità perché anche le animazioni web efficienti hanno limiti di prestazioni. Per questo motivo è sempre importante misurare.
Che cosa sono i frame dell'animazione?
Gli aggiornamenti alla rappresentazione visiva di una pagina richiedono tempo per essere visualizzati. Una modifica visiva porterà a un nuovo frame dell'animazione, che alla fine verrà visualizzato sul display dell'utente.
Mostra l'aggiornamento a un determinato intervallo, in modo che gli aggiornamenti visivi vengano raggruppati. Molti display si aggiornano a intervalli di tempo fissi, ad esempio 60 volte al secondo (ovvero 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 in base alle necessità o persino offrire frame rate completamente variabili.
L'obiettivo di qualsiasi applicazione, come un gioco o un browser, è elaborare tutti questi aggiornamenti visivi batch e produrre un frame di animazione visivamente completo entro la scadenza, ogni volta. Tieni presente che questo obiettivo è completamente distinto da altre importanti attività del browser, come il caricamento rapido dei contenuti dalla rete o l'esecuzione efficiente delle attività JavaScript.
A un certo punto, può diventare troppo difficile completare tutti gli aggiornamenti visivi entro la scadenza assegnata dal display. In questo caso, il browser elimina un frame. Lo schermo non diventa nero, ma si ripete. Vedrai lo stesso aggiornamento visivo per un po' di tempo in più, lo stesso frame dell'animazione che è stato presentato nella precedente opportunità di frame.
In realtà, capita spesso. Potrebbe non essere nemmeno percepibile, soprattutto per i contenuti statici o simili a documenti, comuni in particolare sulla piattaforma web. I frame persi diventano evidenti solo quando ci sono aggiornamenti visivi importanti, come le animazioni, per i quali abbiamo bisogno di un flusso costante di aggiornamenti delle animazioni per mostrare un movimento fluido.
Cosa influisce sui frame dell'animazione?
Gli sviluppatori web possono influire notevolmente sulla capacità di un browser di eseguire il rendering e presentare gli aggiornamenti visivi in modo rapido ed efficiente.
Ecco alcuni esempi:
- Utilizzo di contenuti troppo grandi o che richiedono troppe risorse per essere decodificati rapidamente sul dispositivo di destinazione.
- Utilizzo di troppi livelli che richiedono troppa memoria GPU.
- Definizione di stili CSS o animazioni web eccessivamente complessi.
- Utilizzo di anti-pattern di progettazione che disabilitano le ottimizzazioni del rendering rapido.
- Troppo lavoro JS sul thread principale, il che porta a attività lunghe che bloccano gli aggiornamenti visivi.
Ma come puoi sapere quando un frame di animazione non ha rispettato la scadenza e ha causato un frame perso?
Un metodo possibile è l'utilizzo del
requestAnimationFrame()
polling, che però presenta diversi svantaggi. requestAnimationFrame()
o "rAF",
indica al browser che vuoi eseguire un'animazione e chiede un'opportunità
per farlo prima della fase di rendering successiva della pipeline di rendering. Se
la funzione di callback non viene chiamata al momento previsto, significa che
il rendering non è stato eseguito e uno o più frame sono stati ignorati. Eseguendo il polling e
contando la frequenza con cui viene chiamato 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 polling requestAnimationFrame()
non è una buona idea per diversi motivi:
- Ogni script deve configurare il proprio ciclo di polling.
- Può bloccare il percorso critico.
- Anche se il polling rAF è veloce, può impedire
requestIdleCallback()
di pianificare lunghi blocchi di inattività se utilizzato in modo continuo (blocchi che superano un singolo frame). - Analogamente, la mancanza di blocchi di inattività prolungata impedisce al browser di pianificare altre attività a esecuzione prolungata (come la garbage collection più lunga e altre attività in background o speculative).
- Se il polling viene attivato e disattivato, perderai i casi in cui il budget dei frame è stato superato.
- Il polling segnalerà falsi positivi nei casi in cui il browser utilizza una frequenza di aggiornamento variabile (ad esempio, a causa dello stato di alimentazione o visibilità).
- E, cosa ancora più importante, non acquisisce tutti i tipi di aggiornamenti dell'animazione.
Un carico di lavoro eccessivo sul thread principale può influire sulla capacità di visualizzare i frame dell'animazione. Dai un'occhiata a Jank Sample per vedere come un'animazione basata su rAF, una volta che il thread principale è troppo carico di lavoro (ad esempio il layout), porterà a frame persi e a un numero inferiore di callback rAF, nonché a un FPS inferiore.
Quando il thread principale rallenta, gli aggiornamenti visivi iniziano a essere intermittenti. Che schifo!
Molti strumenti di misurazione si sono concentrati molto sulla capacità del thread principale di produrre in modo tempestivo e sull'esecuzione fluida dei frame di animazione. Ma questa non è tutta la storia. Considera l'esempio seguente:
Il video 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 nell'angolo in alto a sinistra puoi vedere una
corrispondente riduzione degli FPS segnalati a 0.requestAnimationFrame()
Nonostante queste lunghe attività, la pagina continua a scorrere senza problemi. Questo perché sui browser moderni, lo scorrimento è spesso multithread, gestito interamente dal compositore.
Questo è un esempio che contiene contemporaneamente molti frame eliminati nel thread principale, ma ha comunque molti frame di scorrimento inviati correttamente nel thread del compositore. Una volta completata l'attività lunga, l'aggiornamento della pittura del thread principale non offre comunque alcuna modifica visiva. Il polling rAF ha suggerito un calo dei frame a 0, ma visivamente, un utente non sarebbe in grado di notare una differenza.
Per i fotogrammi di animazione, la situazione non è così semplice.
Fotogrammi dell'animazione: aggiornamenti importanti
L'esempio precedente mostra che la storia non si limita a
requestAnimationFrame()
.
Quindi, quando sono importanti gli aggiornamenti delle animazioni e i relativi frame? Ecco alcuni criteri che stiamo prendendo in considerazione e su cui ci piacerebbe ricevere un feedback:
- Aggiornamenti dei thread principale e di composizione
- Aggiornamenti della vernice mancanti
- Rilevamento delle animazioni
- Qualità rispetto alla quantità
Aggiornamenti dei thread principale e di composizione
Gli aggiornamenti dei frame dell'animazione non sono booleani. Non è vero che i frame possono essere completamente eliminati o completamente presentati. Esistono molti motivi per cui un frame di animazione potrebbe essere presentato parzialmente. In altre parole, può avere contemporaneamente alcuni contenuti obsoleti e alcuni nuovi aggiornamenti visivi che vengono presentati.
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 di scorrimento con thread precedente).
Un motivo importante per cui è consigliabile utilizzare le animazioni dichiarative per animare le proprietà composite è che in questo modo un'animazione può essere gestita interamente dal thread del compositore anche quando il thread principale è occupato. Questi tipi di animazioni possono continuare a produrre aggiornamenti visivi in modo efficiente e parallelo.
D'altra parte, potrebbero verificarsi casi in cui un aggiornamento del thread principale diventa finalmente disponibile per la presentazione, ma solo dopo aver mancato diverse scadenze dei frame. Qui il browser avrà alcuni nuovi aggiornamenti, ma potrebbe non essere l'ultimo.
In generale, consideriamo i frame che contengono alcuni nuovi aggiornamenti visivi, senza tutti i nuovi aggiornamenti visivi, come un frame parziale. I frame parziali sono abbastanza comuni. Idealmente, gli aggiornamenti parziali dovrebbero includere almeno gli aggiornamenti visivi più importanti, come le animazioni, ma ciò può avvenire solo se le animazioni sono gestite dal thread del compositore.
Aggiornamenti della vernice mancanti
Un altro tipo di aggiornamento parziale si verifica quando la decodifica e la rasterizzazione di contenuti multimediali come le immagini non sono state completate in tempo per la presentazione del frame.
Oppure, anche se una pagina è perfettamente statica, i browser potrebbero comunque non riuscire a eseguire il rendering degli aggiornamenti visivi durante lo scorrimento rapido. Questo perché i rendering dei pixel dei contenuti oltre la finestra visibile potrebbero essere eliminati per risparmiare memoria GPU. Il rendering dei pixel richiede tempo e potrebbe richiedere più di un singolo frame per eseguire il rendering di tutto dopo uno scorrimento ampio, come uno scorrimento rapido con il dito. Questo metodo è comunemente noto come checkerboarding.
Con ogni opportunità di rendering dei frame, è possibile monitorare la quantità di aggiornamenti visivi più recenti effettivamente visualizzati sullo schermo. La misurazione della capacità di farlo su più frame (o tempo) è nota come throughput dei frame.
Se la GPU è davvero sovraccarica, il browser (o la piattaforma) potrebbe persino iniziare a limitare la velocità con cui tenta gli aggiornamenti visivi e quindi a diminuire i frame rate effettivi. Anche se tecnicamente ciò può ridurre il numero di aggiornamenti dei frame persi, visivamente apparirà comunque come una velocità effettiva dei frame inferiore.
Tuttavia, non tutti i tipi di throughput di frame basso sono negativi. Se la pagina è per lo più inattiva e non sono presenti animazioni attive, una frequenza fotogrammi bassa è altrettanto accattivante dal punto di vista visivo rispetto a una frequenza fotogrammi elevata (e può risparmiare batteria).
Quindi, quando è importante la velocità effettiva dei frame?
Rilevamento delle animazioni
L'elevata velocità effettiva dei frame è importante soprattutto durante i periodi con animazioni importanti. I diversi tipi di animazione dipendono dagli aggiornamenti visivi di un thread specifico (principale, compositore o worker), pertanto l'aggiornamento visivo dipende dal fatto che il thread fornisca l'aggiornamento entro la scadenza. Si dice che un determinato thread influisce sulla fluidità 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 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 il frame rate
(come mostrato sopra) non dovrebbe influire sulle misurazioni della fluidità, poiché non vi è
alcun aggiornamento visivo.
Qualità rispetto alla quantità
Infine, il rilevamento delle animazioni e degli aggiornamenti dei frame delle animazioni è solo una parte della storia, perché acquisisce solo la quantità di aggiornamenti delle animazioni, non la qualità.
Ad esempio, potresti notare un frame rate costante di 60 fps mentre guardi un video. Tecnicamente, la riproduzione è perfettamente fluida, ma il video stesso potrebbe avere un bit rate basso o problemi di buffering della rete. Questo aspetto non viene rilevato direttamente dalle metriche di fluidità dell'animazione, ma può comunque risultare fastidioso per l'utente.
In alternativa, un gioco che sfrutta <canvas>
(magari utilizzando tecniche come
offscreen
canvas per
garantire una frequenza dei fotogrammi costante) potrebbe essere tecnicamente perfetto in termini di
fotogrammi di animazione, pur non riuscendo a caricare asset di gioco di alta qualità nella
scena o mostrando artefatti di rendering.
E, naturalmente, un sito può avere delle animazioni davvero pessime 🙂
Voglio dire, immagino che fossero piuttosto cool per la loro epoca.
Stati di un singolo frame dell'animazione
Poiché i frame potrebbero essere presentati parzialmente o potrebbero verificarsi frame eliminati in modi che non influiscono sulla fluidità, abbiamo iniziato a considerare ogni frame come se avesse un punteggio di completezza o fluidità.
Di seguito è riportato lo spettro di modi in cui interpretiamo lo stato di un singolo frame di animazione, in ordine dal migliore al peggiore:
No Update Desired | Tempo di inattività, ripetizione del frame precedente. |
Completamente presentata | L'aggiornamento del thread principale è stato eseguito entro la scadenza oppure non era necessario. |
Parzialmente presentata | Solo compositore; l'aggiornamento del thread principale ritardato non ha comportato modifiche visive. |
Parzialmente presentata | Solo compositore; il thread principale ha ricevuto un aggiornamento visivo, ma questo non includeva un'animazione che influisce sulla fluidità. |
Parzialmente presentata | Solo compositore; il thread principale ha ricevuto un aggiornamento visivo che influisce sulla fluidità, ma è arrivato un frame precedentemente obsoleto ed è stato utilizzato al suo posto. |
Parzialmente presentata | Solo compositor; senza l'aggiornamento principale desiderato e l'aggiornamento del compositor ha un'animazione che influisce sulla fluidità. |
Parzialmente presentata | Solo compositore, ma l'aggiornamento del compositore non ha un'animazione che influisce sulla fluidità. |
Frame interrotto | Nessun aggiornamento. Non è stato richiesto alcun aggiornamento del compositore e main è stato ritardato. |
Frame interrotto | Era previsto un aggiornamento del compositore, ma è stato ritardato. |
Frame obsoleto | È stato richiesto un aggiornamento, prodotto dal renderer, ma la GPU non lo ha comunque presentato prima della scadenza della sincronizzazione verticale. |
È possibile trasformare questi stati in una sorta di punteggio. Un modo per interpretare questo punteggio è considerarlo una probabilità di essere osservabile dall'utente. Un singolo frame perso potrebbe non essere molto visibile, ma una sequenza di molti frame persi che influiscono sulla fluidità di fila lo è sicuramente.
Riepilogo: una metrica Percentuale di frame eliminati
Anche se a volte può essere necessario analizzare in dettaglio lo stato di ogni frame di animazione, è anche utile assegnare un punteggio rapido "a colpo d'occhio" per un'esperienza.
Poiché i frame potrebbero essere presentati parzialmente e poiché anche gli aggiornamenti dei frame completamente ignorati potrebbero non influire sulla fluidità, vogliamo concentrarci meno sul semplice conteggio dei frame e più sulla misura in cui il browser non è in grado di fornire aggiornamenti visivamente completi quando era importante.
Il modello mentale dovrebbe passare da:
- Frame al secondo, a
- Rilevamento di aggiornamenti di animazione mancanti e importanti, per
- Percentuale di abbandono in un determinato periodo di tempo.
Ciò che conta è la proporzione di tempo di attesa per gli aggiornamenti importanti. Riteniamo che questo corrisponda al modo naturale in cui gli utenti percepiscono la fluidità dei contenuti web nella pratica. Finora abbiamo utilizzato le seguenti metriche come insieme iniziale:
- Percentuale media di fotogrammi eliminati:per tutti i fotogrammi di animazione non inattivi nell'intera sequenza temporale
- Scenario peggiore di percentuale di frame eliminati: misurato su finestre temporali scorrevoli di 1 secondo.
- 95° percentile di percentuale di frame eliminati:misurato su finestre temporali mobili di 1 secondo.
Puoi trovare questi punteggi in alcuni strumenti per sviluppatori di Chrome oggi. Sebbene queste metriche si concentrino solo sulla velocità effettiva complessiva dei frame, stiamo valutando anche altri fattori, come la latenza dei frame.
Fai una prova negli strumenti per sviluppatori.
HUD delle prestazioni
Chromium ha un pratico HUD delle prestazioni nascosto dietro un flag
(chrome://flags/#show-performance-metrics-hud
). Al suo interno, puoi trovare punteggi
in tempo reale per elementi come i Segnali web essenziali e anche alcune definizioni sperimentali
per la fluidità delle animazioni basate sulla percentuale di frame persi nel tempo.
Statistiche di rendering del frame
Attiva "Statistiche rendering frame" in DevTools tramite le impostazioni di rendering per visualizzare una visualizzazione in tempo reale dei nuovi frame di animazione, codificati con colori diversi per distinguere gli aggiornamenti parziali da quelli dei frame completamente eliminati. Gli fps segnalati si riferiscono solo ai frame presentati completamente.
Visualizzatore frame nelle registrazioni del profilo delle prestazioni di DevTools
Il pannello Rendimento di DevTools ha sempre avuto un visualizzatore di frame. Tuttavia, era diventato un po' fuori sincrono rispetto al funzionamento della pipeline di rendering moderna. Sono stati apportati molti miglioramenti recenti, anche nell'ultima versione di Chrome Canary, che riteniamo semplificheranno notevolmente il debug dei problemi di animazione.
Oggi noterai che i frame nel visualizzatore di frame sono allineati meglio ai limiti di sincronizzazione verticale e codificati per colore in base allo stato. Non è ancora disponibile una visualizzazione completa di tutte le sfumature descritte sopra, ma abbiamo in programma di aggiungerne altre nel prossimo futuro.
Tracciamento di Chrome
Infine, con Chrome Tracing, lo strumento ideale per analizzare in dettaglio i problemi, puoi registrare una traccia di "Rendering dei contenuti web" tramite la nuova interfaccia utente Perfetto (o about:tracing
) e analizzare in dettaglio la pipeline grafica di Chrome. Può essere un compito arduo, ma di recente sono state aggiunte alcune funzionalità a Chromium per semplificarlo. Puoi trovare una panoramica di ciò che è disponibile nel documento Ciclo di vita di un frame.
Tramite gli eventi di traccia puoi determinare in modo definitivo:
- Quali animazioni sono in esecuzione (utilizzando eventi denominati
TrackerValidation
). - Ottenere la sequenza temporale esatta dei fotogrammi dell'animazione (utilizzando eventi denominati
PipelineReporter
). - Per gli aggiornamenti delle animazioni instabili, scopri esattamente cosa impedisce all'animazione di essere eseguita più velocemente (utilizzando le suddivisioni degli eventi all'interno degli eventi
PipelineReporter
). - Per le animazioni basate sull'input, vedi quanto tempo è necessario per ricevere un aggiornamento visivo
(utilizzando eventi denominati
EventLatency
).
Passaggi successivi
L'iniziativa Web Vitals mira a fornire metriche e indicazioni per creare esperienze utente ottimali sul web. Le metriche basate su test di laboratorio come il Total Blocking Time (TBT) sono fondamentali per rilevare e diagnosticare potenziali problemi di interattività. Stiamo pianificando di progettare una metrica simile basata sul lab per la fluidità dell'animazione.
Ti aggiorneremo man mano che continuiamo a lavorare sulle idee per progettare una metrica completa basata sui dati dei singoli frame di animazione.
In futuro, vorremmo anche progettare API che consentano di misurare la fluidità dell'animazione in modo efficiente per gli utenti reali nel campo e in laboratorio. Continua a seguirci per non perderti gli aggiornamenti.
Feedback
Siamo entusiasti di tutti i recenti miglioramenti e degli strumenti per sviluppatori che sono stati implementati in Chrome per misurare la fluidità delle animazioni. Prova questi strumenti, confronta le tue animazioni e facci sapere dove ti portano.
Puoi inviare i tuoi commenti al gruppo Google web-vitals-feedback con "[Metriche di fluidità]" nella riga dell'oggetto. Non vediamo l'ora di conoscere la tua opinione.