Introdução
Pintar os elementos de um site ou aplicativo pode ficar muito caro e ter um efeito negativo no desempenho do ambiente de execução. Neste artigo, vamos analisar rapidamente o que pode acionar a pintura no navegador e como evitar que pinturas desnecessárias ocorram.
Pintura: um tour super-rápido
Uma das principais tarefas que um navegador precisa realizar é converter o DOM e o CSS em pixels na tela, e isso é feito por um processo bastante complexo. Ele começa lendo a marcação e, a partir dela, cria uma árvore DOM. Ele faz algo semelhante com o CSS e, a partir disso, cria o CSSOM. O DOM e o CSSOM são combinados e, eventualmente, chegamos a uma estrutura em que podemos começar a pintar alguns pixels.
O processo de pintura em si é interessante. No Chrome, essa árvore combinada de DOM e CSS é rasterizada por um software chamado Skia. Se você já usou a API Skia do elemento canvas
, ela vai parecer muito familiar. Há muitas funções no estilo moveTo
e lineTo
, além de várias mais avançadas. Basicamente, todos os elementos que precisam ser pintados são destilados para uma coleção de chamadas Skia que podem ser executadas, e a saída é um monte de bitmaps. Esses bitmaps são enviados para a GPU, que os compõe para mostrar a imagem final na tela.

A carga de trabalho do Skia é diretamente afetada pelos estilos que você aplica aos elementos. Se você usar estilos logicamente pesados, o Skia terá mais trabalho. Colt McAnlis escreveu um artigo sobre como o CSS afeta o peso da renderização da página. Leia para saber mais.
Com tudo isso, o trabalho de pintura leva tempo para ser realizado, e se não for reduzido, vamos ultrapassar o orçamento de frame de aproximadamente 16 ms. Os usuários vão notar que perdemos frames e vão considerar isso como um problema, o que prejudica a experiência do usuário no app. Não queremos isso, então vamos ver que tipo de coisas exigem a pintura e o que podemos fazer a respeito.
Rolagem
Sempre que você rola para cima ou para baixo no navegador, é necessário repintar o conteúdo antes que ele apareça na tela. Se tudo correr bem, essa será apenas uma área pequena, mas, mesmo que seja o caso, os elementos que precisam ser desenhados podem ter estilos complexos aplicados. Só porque você tem uma área pequena para pintar não significa que isso vai acontecer rapidamente.
Para saber quais áreas estão sendo repintadas, use o recurso "Mostrar retângulos de pintura" nas Ferramentas do desenvolvedor do Chrome. Basta clicar na pequena engrenagem no canto inferior direito. Em seguida, com o DevTools aberto, basta interagir com a página para ver retângulos piscando mostrando onde e quando o Chrome pintou uma parte da página.

O desempenho de rolagem é fundamental para o sucesso do seu site. Os usuários percebem quando o site ou aplicativo não rola bem e não gostam disso. Portanto, temos um interesse pessoal em manter o trabalho de pintura leve durante a rolagem para que os usuários não vejam o travamento.
Eu já escrevi um artigo sobre a performance de rolagem. Leia-o para saber mais sobre os detalhes da performance de rolagem.
Interações
As interações são outra causa do trabalho de pintura: passar o cursor, clicar, tocar, arrastar. Sempre que o usuário realizar uma dessas interações, por exemplo, passar o cursor, o Chrome terá que repintar o elemento afetado. E, assim como na rolagem, se uma pintura grande e complexa for necessária, a taxa de frames vai cair.
Todo mundo quer animações de interação legais e suaves. Então, vamos verificar se os estilos que mudam na nossa animação estão nos custando muito tempo.
Uma combinação infeliz

O que acontece se eu rolar a tela e mover o mouse ao mesmo tempo? É perfeitamente possível que eu inadvertentemente "interaja" com um elemento ao rolar para baixo, acionando uma pintura cara. Isso, por sua vez, poderia aumentar meu orçamento de frames de cerca de 16,7 ms (o tempo que precisamos ficar abaixo disso para atingir 60 frames por segundo). Criei uma demonstração para mostrar exatamente o que quero dizer. Ao rolar e mover o mouse, os efeitos de passar o cursor vão aparecer. Vamos ver o que as Ferramentas do desenvolvedor do Chrome mostram:

Na imagem acima, é possível ver que as Ferramentas do desenvolvedor estão registrando o trabalho de pintura quando passo o cursor sobre um dos blocos. Para mostrar o ponto, usei alguns estilos super pesados na minha demonstração, e estou ultrapassando meu orçamento de frames. A última coisa que eu quero é ter que fazer esse trabalho de pintura desnecessariamente, especialmente durante um rolagem quando há outro trabalho a ser feito.
Como podemos evitar que isso aconteça? A correção é muito simples de implementar. O truque aqui é anexar um manipulador scroll
que desativará os efeitos de passar o cursor e definirá um timer para ativá-los novamente. Isso significa que garantimos que, ao rolar, não será necessário realizar pinturas de interação caras. Quando você para por tempo suficiente, consideramos que é seguro reativá-los.
O código fica assim:
// 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');
}
Como você pode ver, usamos uma classe no corpo para rastrear se os efeitos de passar o cursor são "permitidos" ou não, e os estilos subjacentes dependem da presença dessa classe:
/* Expect the hover class to be on the body
before doing any hover effects */
.hover .block:hover {
…
}
Isso é tudo.
Conclusão
O desempenho de renderização é essencial para que os usuários aproveitem o app. Por isso, sempre tente manter a carga de trabalho de pintura abaixo de 16 ms. Para ajudar você a fazer isso, é necessário integrar usando as DevTools durante o processo de desenvolvimento para identificar e corrigir gargalos à medida que eles surgem.
Interações acidentais, principalmente em elementos com muita pintura, podem ser muito caras e acabar com o desempenho da renderização. Como você viu, podemos usar um pequeno trecho de código para corrigir isso.
Analise seus sites e aplicativos. Eles precisam de um pouco de proteção?