Evitar pinturas desnecessárias - Edição de GIFs animados

Evitar tintas é essencial para conseguir um frame rate suave, especialmente em dispositivos móveis. Às vezes, no entanto, as tintas aparecem nos lugares mais incomuns. Este artigo analisa por que os GIFs animados podem gerar pinturas desnecessárias e a correção extremamente simples que você pode aplicar.

Camadas de amor

Como você provavelmente sabe, os navegadores modernos podem pintar grupos de elementos DOM em "imagens", chamadas de camadas. Às vezes, há uma camada para a página inteira. Outras vezes, há centenas ou, em casos raros, milhares.

Quando elementos DOM são agrupados em uma camada e um dos elementos muda visualmente, acabamos tendo que pintar não apenas o elemento alterado, mas todos os outros elementos na camada que se sobrepõem a ele. Pintar uma coisa em cima de outra faz com que os pixels substituídos sejam "perdidos" para sempre. Se quiser recuperar os pixels originais, será necessário repintá-los.

Por isso, às vezes, queremos isolar um elemento dos outros para que, quando ele for pintado, não precisemos pintar de novo os outros elementos que não mudaram. Por exemplo, ao combinar um cabeçalho de página fixo com conteúdo rolável, é necessário repintar o cabeçalho toda vez que o conteúdo for rolado, assim como o conteúdo recém-visível. Ao colocar o cabeçalho em uma camada separada, o navegador pode otimizar a rolagem. Quando você rola a tela, o navegador pode mover as camadas, provavelmente com a ajuda da GPU, e evitar a repintura de qualquer uma delas.

Cada camada adicional aumenta o consumo de memória e a sobrecarga de desempenho. Por isso, o objetivo é agrupar a página no menor número possível de camadas, mantendo um bom desempenho.

O que tudo isso tem a ver com GIFs animados?

Vamos dar uma olhada nesta imagem:

Um app da Web dividido em quatro camadas.
Figura 1: um app da Web dividido em quatro camadas.

Essa é uma possível configuração de camada para um app simples. Há quatro camadas: três delas (camadas 2 a 4) são elementos de interface e a camada de fundo é um carregador, que é um GIF animado. No fluxo normal, você mostra o carregador (camada 1) enquanto seu aplicativo é carregado e, quando tudo terminar, as outras camadas serão mostradas. Mas aqui está a chave: você precisa ocultar o GIF animado.

Mas por que preciso ocultá-lo?!

Boa pergunta. Em um mundo perfeito, o navegador simplesmente verificaria a visibilidade do GIF e evitaria pintar automaticamente. Infelizmente, verificar se o GIF animado está obscurecido ou visível na tela costuma ser mais caro do que simplesmente pintar o GIF, por isso ele é pintado.

No melhor dos casos, o GIF está na própria camada e o navegador só precisa pintar e fazer upload dele para a GPU. Mas, na pior das hipóteses, todos os elementos podem ser agrupados em uma única camada, e o navegador precisa repintar cada elemento. Depois de concluído, ele ainda precisa fazer upload de tudo para a GPU. Todo esse trabalho ocorre para cada frame do GIF, apesar do fato de que o usuário não consegue ver o GIF.

Em computadores, esse tipo de comportamento de pintura provavelmente é aceitável porque as CPUs e as GPUs são mais potentes e há muita largura de banda para transferir dados entre as duas. No entanto, em dispositivos móveis, a pintura é extremamente cara, então você precisa tomar muito cuidado.

Quais navegadores serão afetados?

Como geralmente acontece, o comportamento varia de acordo com o navegador. Atualmente, o Chrome, o Safari e o Opera são repinturados, mesmo que o GIF esteja obscurecido. O Firefox, por outro lado, descobre que o GIF está obscurecido e não precisa ser repintado. O Internet Explorer continua sendo uma caixa preta, e mesmo no IE11, já que as ferramentas F12 ainda estão sendo desenvolvidas, não há indicação de que uma repintura esteja ocorrendo.

Como saber se tenho esse problema?

A maneira mais fácil é usar a opção "Mostrar retângulos de pintura" no Chrome DevTools. Carregue o DevTools, pressione a engrenagem no canto inferior direito (Ícone de engrenagem) e escolha Show paintRectangles na seção Rendering.

Ativar a opção "Mostrar retângulos de pintura" no Chrome DevTools
Figura 2: ativação de "Mostrar retângulos de pintura" no Chrome DevTools.

Agora você só precisa procurar um retângulo vermelho como este:

O DevTools "Show Paint Rectangles" sugere problemas de GIFs animados com um retângulo vermelho.
Figura 3: "Show Paint Rectangles" do DevTools sugere problemas de GIF animado com um retângulo vermelho.

A pequena caixa vermelha na tela mostra que o Chrome está repintando algo. Você sabe que há um GIF de carregador escondido atrás dos outros elementos. Então, quando você vê uma caixa vermelha como essa, precisa ocultar os elementos visíveis e verificar se deixou o GIF animado girando. Em caso afirmativo, será necessário colocar CSS ou JavaScript no local para aplicar display: none ou visibility: hidden a ele ou ao elemento pai. É claro que se for apenas uma imagem de plano de fundo, você deve removê-la.

Se você quiser ver um exemplo desse comportamento em um site ativo, confira o Allegro, onde a imagem de cada produto tem um GIF de carregador escurecido em vez de oculto explicitamente.

Conclusão

Alcançar 60 fps significa fazer apenas o que é necessário para renderizar a página. A remoção de tintas em excesso é uma etapa essencial para atingir essa meta. Os GIFs animados que ficam ativos podem acionar pinturas desnecessárias, algo que pode ser encontrado e depurado facilmente com a ferramenta Show Paint Retângulos do DevTools.

Agora, você não deixou aquele GIF animado do carregador de gatinho ativo para sempre, não é?