Eventos de entrada alinhados

Dave Tapuska
Dave Tapuska

Texto longo, leia o resumo

  • O Chrome 60 reduz o tempo de carregamento diminuindo a frequência de eventos, melhorando a consistência do tempo de frame.
  • O método getCoalescedEvents(), introduzido no Chrome 58, oferece a mesma quantidade de informações de eventos que você já tinha.

Oferecer uma experiência do usuário tranquila é importante para a Web. O tempo entre o recebimento de um evento de entrada e o momento em que os recursos visuais são atualizados é importante, e geralmente fazer menos trabalho é importante. Nas últimas versões do Chrome, reduzimos a latência de entrada nesses dispositivos.

Para melhorar a fluidez e o desempenho, no Chrome 60, estamos fazendo uma mudança que faz com que esses eventos ocorram com uma frequência menor, aumentando a granularidade das informações fornecidas. Assim como quando o Jelly Bean foi lançado e trouxe o Choreographer, que alinha a entrada no Android, estamos trazendo a entrada alinhada ao frame para a Web em todas as plataformas.

Mas às vezes você precisa de mais eventos. No Chrome 58, implementamos um método chamado getCoalescedEvents(), que permite que seu aplicativo recupere o caminho completo do ponteiro mesmo quando ele recebe menos eventos.

Vamos falar primeiro sobre a frequência de eventos.

Como diminuir a frequência de eventos

Vamos entender alguns conceitos básicos: as telas sensíveis ao toque fornecem entrada de 60 a 120 Hz, e os mouses geralmente fornecem entrada de 100 Hz (mas podem variar até 2.000 Hz). No entanto, a taxa de atualização típica de um monitor é de 60 Hz. O que isso significa? Isso significa que recebemos entradas em uma taxa mais alta do que a taxa de atualização da tela. Vamos analisar uma linha do tempo de desempenho das ferramentas do desenvolvedor para um app de pintura de tela simples.

Na imagem abaixo, com a entrada alinhada a requestAnimationFrame() desativada, é possível ver vários blocos de processamento por frame com um tempo de frame inconsistente. Os pequenos blocos amarelos indicam o teste de acerto para coisas como o destino do evento DOM, o envio do evento, a execução do JavaScript, a atualização do nó sobreposto e possivelmente o recalculo do layout e dos estilos.

Uma linha do tempo de performance mostrando um tempo de frame inconsistente

Por que estamos fazendo um trabalho extra que não causa atualizações visuais? O ideal é não fazer nenhum trabalho que não beneficie o usuário. A partir do Chrome 60, o pipeline de entrada vai atrasar o envio de eventos contínuos (wheel, mousewheel, touchmove, pointermove, mousemove) e enviá-los logo antes do callback requestAnimationFrame() ocorrer. Na imagem abaixo (com o recurso ativado), você vê um tempo de frame mais consistente e menos tempo de processamento de eventos.

Estamos realizando um experimento com esse recurso ativado nos canais Canary e Dev e descobrimos que realizamos 35% menos testes de hit, o que permite que a linha de execução principal esteja pronta para ser executada com mais frequência.

Uma observação importante que os desenvolvedores da Web precisam saber é que qualquer evento discreto (como keydown, keyup, mouseup, mousedown, touchstart, touchend) que ocorre será enviado imediatamente com todos os eventos pendentes, preservando a ordem relativa. Com esse recurso ativado, grande parte do trabalho é simplificada no fluxo normal de loop de eventos, oferecendo um intervalo de entrada consistente. Isso traz eventos contínuos em conformidade com os eventos scroll e resize, que já foram simplificados no fluxo de repetição de eventos no Chrome.

Uma linha do tempo de desempenho mostrando um tempo de frame relativamente consistente.

Descobrimos que a grande maioria dos aplicativos que consomem esses eventos não usa a frequência mais alta. O Android já alinha eventos há vários anos, então nada é novo, mas os sites podem ter eventos menos granulares em plataformas para computador. Sempre houve um problema com linhas de execução principais instáveis que causam interrupções na fluidez da entrada, o que significa que você pode notar saltos na posição sempre que o aplicativo estiver trabalhando, o que torna impossível saber como o ponteiro passou de um ponto para outro.

O método getCoalescedEvents()

Como eu disse, há raros cenários em que o aplicativo prefere saber o caminho completo do ponteiro. Para corrigir o caso em que você vê saltos grandes e a frequência reduzida de eventos, no Chrome 58, lançamos uma extensão para eventos de ponteiro chamada getCoalescedEvents(). Confira abaixo um exemplo de como o jank na linha de execução principal é oculto do aplicativo se você usar essa API.

Comparação de eventos padrão e unificados.

Em vez de receber um único evento, você pode acessar a matriz de eventos históricos que o causou. O Android, o iOS e o Windows têm APIs muito semelhantes nos SDKs nativos, e estamos expondo uma API semelhante para a Web.

Normalmente, um app de desenho pode ter desenhado um ponto observando os deslocamentos no evento:

window.addEventListener("pointermove", function(event) {
    drawPoint(event.pageX, event.pageY);
});

Esse código pode ser facilmente alterado para usar a matriz de eventos:

window.addEventListener("pointermove", function(event) {
    var events = 'getCoalescedEvents' in event ? event.getCoalescedEvents() : [event];
    for (let e of events) {
    drawPoint(e.pageX, e.pageY);
    }
});

Nem todas as propriedades nos eventos agrupados são preenchidas. Como os eventos agrupados não são realmente enviados, mas apenas acompanham o percurso, eles não são testados. Alguns campos, como currentTarget e eventPhase, terão valores padrão. Chamar métodos relacionados ao envio, como stopPropagation() ou preventDefault(), não terá efeito no evento pai.