TL;DR
- Chrome 60 riduce il jitter riducendo la frequenza degli eventi, migliorando così la consistenza della temporizzazione dei fotogrammi.
- Il metodo
getCoalescedEvents()
, introdotto in Chrome 58, fornisce la stessa gamma di informazioni sugli eventi che hai sempre avuto.
Offrire un'esperienza utente fluida è importante per il web. È importante l'intervallo di tempo tra la ricezione di un evento di input e l'aggiornamento effettivo delle immagini e, in generale, è importante fare meno lavoro. Nelle ultime release di Chrome abbiamo ridotto la latenza di input su questi dispositivi.
Nell'interesse della fluidità e delle prestazioni, in Chrome 60 stiamo apportando una modifica che fa sì che questi eventi si verifichino con una frequenza inferiore, aumentando al contempo la granularità delle informazioni fornite. Come quando è stato rilasciato Jelly Bean e abbiamo introdotto Choreographer, che allinea l'input su Android, stiamo portando l'input allineato ai frame sul web su tutte le piattaforme.
Tuttavia, a volte sono necessari più eventi. Pertanto, in
Chrome 58, abbiamo implementato
un metodo chiamato
getCoalescedEvents()
, che consente alla tua applicazione di recuperare il percorso completo del cursore anche mentre riceve meno eventi.
Parliamo innanzitutto della frequenza degli eventi.
Riduzione della frequenza degli eventi
Vediamo alcuni concetti di base: i touchscreen inviano input a 60-120 Hz e i mouse in genere a 100 Hz (ma possono arrivare fino a 2000 Hz). Tuttavia, la frequenza di aggiornamento tipica di un monitor è di 60 Hz. Che cosa significa? Ciò significa che riceviamo input a una frequenza superiore a quella con cui aggiorniamo effettivamente il display. Vediamo quindi una sequenza temporale delle prestazioni di devtools per una semplice app di pittura su canvas.
Nell'immagine seguente, con l'input allineato a requestAnimationFrame()
disattivato, puoi vedere più blocchi di elaborazione per frame con un tempo di frame incoerente.
I piccoli blocchi gialli indicano i test di hit per elementi quali il target dell'evento DOM, l'invio dell'evento, l'esecuzione di JavaScript, l'aggiornamento del nodo visualizzato e, eventualmente, il nuovo calcolo di layout e stili.
Perché, quindi, ci stiamo impegnando in un lavoro extra che non comporta aggiornamenti visivi? Idealmente, non vogliamo fare nulla che non sia vantaggioso per l'utente. A partire da Chrome 60, la pipeline di input ritarderà l'invio degli eventi continui (wheel
, mousewheel
, touchmove
, pointermove
, mousemove
) e li invierà subito prima che si verifichi il callback requestAnimationFrame()
. Nella figura seguente (con la funzionalità attivata), viene visualizzato un tempo di frame più costante e un tempo di elaborazione degli eventi inferiore.
Abbiamo condotto un esperimento con questa funzionalità attivata sui canali Canary e Dev e abbiamo scoperto che eseguiamo il 35% in meno di test di hit, il che consente al thread principale di essere pronto per l'esecuzione più spesso.
Una nota importante che gli sviluppatori web devono tenere presente è che qualsiasi evento distinto (ad esempio keydown
, keyup
, mouseup
, mousedown
, touchstart
, touchend
) che si verifica verrà inviato immediatamente insieme a eventuali eventi in attesa, preservando l'ordine relativo. Con questa funzionalità abilitata, gran parte del lavoro viene semplificata nel normale flusso del loop di eventi, fornendo un intervallo di input coerente. In questo modo, gli eventi continui vengono equiparati agli eventi scroll
e resize
, che sono già stati semplificati nel flusso del loop di eventi in Chrome.
Abbiamo riscontrato che la maggior parte delle applicazioni che utilizzano questi eventi non ha bisogno della frequenza più elevata. Android ha già allineato gli eventi da diversi anni, quindi non c'è nulla di nuovo, ma i siti potrebbero registrare eventi meno granulari sulle piattaforme desktop. È sempre stato presente un problema con i thread principali irregolari che causano problemi di fluidità dell'input, il che significa che potresti vedere salti nella posizione ogni volta che l'applicazione è in esecuzione, rendendo impossibile sapere come il cursore è passato da un punto all'altro.
Il metodo getCoalescedEvents()
Come ho detto, esistono rari scenari in cui l'applicazione preferisce conoscere il percorso completo del cursore. Per risolvere il problema dei salti eccessivi e della frequenza ridotta degli eventi, in Chrome 58 abbiamo lanciato un'estensione per gli eventi del cursore chiamata getCoalescedEvents()
. Di seguito è riportato un esempio di come il jitter nel thread principale viene nascosto all'applicazione se utilizzi questa API.
Anziché ricevere un singolo evento, puoi accedere all'array di eventi storici che lo hanno causato. Android, iOS e Windows hanno tutti API molto simili nei loro SDK nativi e stiamo esponendo un'API simile sul web.
In genere, un'app di disegno potrebbe aver disegnato un punto guardando gli offset nell'evento:
window.addEventListener("pointermove", function(event) {
drawPoint(event.pageX, event.pageY);
});
Questo codice può essere facilmente modificato per utilizzare l'array di eventi:
window.addEventListener("pointermove", function(event) {
var events = 'getCoalescedEvents' in event ? event.getCoalescedEvents() : [event];
for (let e of events) {
drawPoint(e.pageX, e.pageY);
}
});
Tieni presente che non tutte le proprietà degli eventi uniti sono compilate. Poiché gli eventi uniti non vengono effettivamente inviati, ma sono solo di supporto, non vengono sottoposti a test di hit. Alcuni campi, come currentTarget
e eventPhase
, avranno i valori predefiniti. La chiamata di metodi di gestione degli eventi come stopPropagation()
o preventDefault()
non avrà alcun effetto sull'evento principale.