Tudo o que você precisa saber sobre a propriedade CSS will-change

visualizações Publicado em: 27/04/2015

Introdução

Se você já percebeu aquele efeito "flicker" (tremido), em navegadores com engine WebKit, ao renderizar certas funcionalidades CSS, especialmente transformações e animações então é muito provável que você já tenha ouvido falar de "hardware acceleration".

A CPU, GPU, e Hardware Acceleration

Em poucas palavras, Hardware Acceleration (Aceleração de Hardware) é a entrada em cena da GPU (Graphics Processing Unit) com a finalidade de auxiliar a CPU (Central Processing Unit) na tarefa de processamento e renderização do trabalho pesado quando assim for requerido. Toda vez que Hardware Acceleration auxilia uma operação CSS há um ganho de velocidade no processamento e como consequência a página é renderizada mais rapidamente.

Como o próprio nome sugere, GPU e CPU são unidades de processamento. A CPU está localizada na placa-mãe do computador e é responsável pelo processamento de quase tudo, sendo também conhecida como o cérebro do computador. A GPU está localizada na placa gráfica do computador e é responsável pelo processamento e renderização de gráficos. A GPU é projetada para realizar cálculos matemáticos e geométricos complexos necessários para renderização de gráficos. Assim, transferindo operações para a GPU pode render enormes ganhos de desempenho e também reduzir a demanda de CPU em dispositivos móveis.

Hardware acceleration (a.k.a. GPU acceleration) baseia-se em um modelo de layers que é usado pelo navegador para renderizar a página. Toda vez que determinadas operações (por exemplo: transformações CSS 3D) são aplicadas a um elemento da página, o elemento é movido para um "layer" próprio dentro do qual pode ser renderizado independentemente do restante da página e ser composited in (desenhado na tela) mais tarde. Se em uma página a única mudança que ocorre é a tranformação CSS 3D de um elemento ela é renderizada em seu próprio layer não havendo necessidade de re-renderizar o restante da página e como consequência há um ganho de velocidade. Convém notar que somente as transformações CSS 3D criam seu próprio layer; transformações CSS 2D não criam.

Animações, transformações e transições CSS não são aceleradas na GPU automaticamente, mas renderizadas pela engine, mais lenta, do navegador. No entanto, alguns navegadores provêem aceleração de hardware para algumas propriedades CSS com a finalidade de incrementar o desempenho na renderização. Por exemplo: a propriedade CSS opacity é uma das propriedades que podem ser aceleradas pelo fato de a GPU poder manipular esta propriedade com facilidade. Se você optar por modificar a opacidade em lugar de criar uma transformação ou animação equivalente os navegadores atuais vão passar a tarefa para a GPU e isso tornará o processamento bem mais rápido. De todas as propriedades CSS a opacidade é a mais performática para se trabalhar e você não terá problemas de performance. Outra propriedade CSS hardware acelerada é a transformação CSS 3D.

O "velho": Hack translateZ() (ou translate3d())

Para enganar o navegador fazendo com que ele habilitasse a hardware acceleration nos acostumamos a usar o chamado hack translateZ() (ou translate3d()) (também conhecido como hack da transformação nula). O hack consiste em se definir para o elemento a animar uma transformação 3D simples que não cause qualquer transformação ao elemento no espaço tri-dimensional. Por exemplo: para habilitar a hardware acceleration na animação de um elemento no espaço bi-dimensional criamos uma declaração CSS para o elemento como a mostrada a seguir:

transform: translate3d(0, 0, 0);

Hardware-accelerating uma operação cria o que é conhecido como composite layer para o elemento, transferindo o processamento da operação para a GPU. Contudo, forçar a criação do layer com hack nem sempre é uma escolha acertada para solucionar determinados problemas de performance em uma página. Técnicas de criação de layers podem aumentar a velocidade no carregamento da página, mas há um custo: elas ocupam espaço de memória RAM e GPU (que são limitadas em dispositivos móveis). Usar indiscriminadamente tais técnicas pode causar um impacto bastante negativo, especialmente em dispositivos móveis. Assim, você deve usar tais técnicas conscientemente certificando-se de que a hardware-acceleration realmente está incrementando a performance da página e não criando um ponto crítico na performance.

Com a finalidade de evitar hacks foi criada uma nova propriedade CSS que permite informar ao navegador, com antecedência, quais são os tipos de operações que serão realizadas em um elemento da página. Com essa informação o navegador é capaz de manipular e processar com antecedência o trabalho pesado necessário, por exemplo: para uma animação o processamento é feito antes que a animação comece. Trata-se da nova propriedade CSS denominada will-change.

O "novo": A gloriosa propriedade CSS will-change

A propriedade CSS will-change destina-se a informar ao navegador quais serão as modificações sofridas por um elemento antes que elas ocorram de modo a que o navegador possa configurar as otimizações necessárias de processamento evitando impacto negativo na capacidade de resposta da página. A renderização da página é mais rápida resultando em melhor experiência para o usuário.

Como vimos anteriormente, definir uma transformação 3D em um elemento coloca-o em um novo layer antes que ele seja renderizado. Contudo, essa técnica é expensiva e assim, ela própria pode retardar o início da animação por uma fração de segundo suficiente para causar o fenômeno "flicker".

Para evitar aquele fenômeno devemos informar ao navegador sobre as modificações que estão por vir um tempo antes que elas ocorram. Com isso o navegador disporá do tempo necessário para processar as modificações de modo a que quando elas ocorram o layer já esteja pronto e a animação em condições de ser renderizada imediatamente.

A propriedade CSS will-change dá, ao navegador, com antecedência, a dica das modificações e para isso é suficiente a simples declaração CSS mostrada a seguir:

will-change: transform;

Essa propriedade CSS permite que você informe ao navegador sobre a ocorrência de mais de uma modificação em um elemento. Por exemplo: se pretendemos modificar a posição do elemento dentro da área visível, seus conteúdos, ou qualquer outra propriedade CSS basta informar o nome da propriedade a modificar separando-as com vírgula, tal como mostrado a seguir para uma transformação e uma modificação de opacidade:

will-change: transform, opacity;

Definir exatamente o que você pretende modificar permite ao navegador decidir sobre a melhor maneira de otimizar as modificações e sem dúvida esta é a melhor maneira de incrementar velocidade sem necessidade de hacks que poderão ser ou não ser necessários ou eficientes.

Aplicar will-change a um elemento tem alguma influência sobre ele além de alertar o navegador sobre modificações que serão feitas no elemento?

A resposta é: depende da propriedade a ser modificada. Se qualquer valor CSS que não seja o valor inicial da propriedade a modificar criar um contexto de empilhamento para o elemento, definir will-change para aquela propriedade criará o contexto de empilhamento para o elemento.

Por exemplo: as propriedades clip-path e opacity quando definidas com valores diferentes dos seus valores CSS iniciais criam um contexto de empilhamento para o elemento aos quais são aplicadas. Assim, usar uma ou ambas as propriedades como valor para will-change criará um contexto de empilhamento para o elemento ao qual forem aplicadas antes da modificação. Esse comportamento ocorre com todas as propriedades que criam um contexto de empilhamento em um elemento.

Algumas propriedades podem criar um contexto de bloco de conteúdo para um elemento. Por exemplo: definir transform para um elemento cria um bloco de conteúdo para todos os seus elementos descendentes posicionados, inclusive para aqueles para os quais definiu-se position: fixed. Assim, se uma propriedade criar um contexto de bloco de conteúdo, definí-la como valor de will-change criará aquele contexto para elementos descendentes posicionados.

Estas (criação de contexto de empilhamento e de bloco de conteúdo) são as duas situações em que will-change causa outro efeito em um elemento, além de dar uma dica ao navegador sobre futuras modificações em um elemento.

Usar will-change: faça e não faça

Sabendo do que will-change é capaz a primeira conclusão é: "Faça o navegador otimizar TUDO!". Esse pensamento faz todo sentido, certo? Quem não quer que todas as modificações sejam otimizadas e estejam prontas para serem renderizadas?

Por mais poderosa e genial que will-change seja, não é diferente de nenhuma das demais funcionalidades poderosas, use-a com responsabilidade. Use will-change com sabedoria, pois do contrário você poderá criar problemas de performance que acabarão por travar sua página.

Tal como ocorre com outras técnicas de incremento de performance will-change tem seus efeitos colaterais que não são imediatamente detetáveis (afinal de contas trata-se de uma maneira de "conversar" com o navegador por trás das cenas) e assim seu uso pode ser complicado. A seguir algumas dicas de como usar esta propriedade sem risco de cair em armadilhas causadas pelo mal uso e tirar o melhor proveito dela.

Não declare will-change para muitas modificações ou elementos

Como dissemos anteriormente pode ser tentador e fazer todo o sentido informar ao navegador as modificações que ocorrerão a todas as propriedades em todos os elements definindo a seguinte regra CSS:

*,
*::before,
*::after {
	will-change: all;
}

Por melhor que esta regra CSS possa parecer (para mim, em um primeiro momento, também parecia muito boa e fazia todo sentido), na verdade ela é altamente prejudicial além de inválida. Não somente a palavra-chave all é um valor inválido para a propriedade will-change (veremos os valores válidos adiante neste artigo), mas também, uma regra geral como esta seria inútil. O navegador tentaria otimizar tudo que pudesse (lembra de opacity e 3D transforms?). Uma regra geral como esta não modificaria nada ou ajudaria de alguma forma. O fato é que causaria um enorme prejuizo pois o pesado processamento para otimizar tudo com uso de will-change demandaria muito da máquina e acabaria por tornar o carregamento da página muito lento ou até mesmo causar seu travamento.

Em outras palavras, colocar o navegador em guarda para modificações que possam ou não ocorrer é uma má ideia e causará mais prejuizo que benefício. Não faça isso.

Dê tempo para o navegador trabalhar

A propriedade will-change (em tradução livre: será modificado) tem esse nome por razões óbvias: ela se destina a informar ao navegador modificações que irão ocorrer e não modificações que estão ocorrendo. Ao usar will-change estaremos instruindo o navegador a fazer certas otimizações para modificações que declaramos e o navegador necessita de tempo para processar as otimizações de modo a que quando elas ocorram possam ser aplicadas sem qualquer retardo.

Declarar will-change para um elemento imediatamente antes da ocorrência da modificação tem pouco ou nenhum efeito. (Isso pode ser pior do que nada declarar. O custo a pagar pode ser a criação de um novo layer em um momento em que ele não é mais útil!). Por xemplo: se uma modificação ocorre no estado hover o código a seguir:

.element:hover {
	will-change: transform;
	transition: transform 2s;
	transform: rotate(30deg) scale(1.5);
}

...instrui o navegador a otimizar modificações que já estão em andamento. Isso é inútil e contraria o conceito de will-change. Você deve encontrar uma maneira de instruir o navegador pelo menos um istante antes da ocorrência da modificação e definir will-change naquele momento.

Por exemplo: se um elemento vai sofrer modificações quando for clicado definir will-change quando o ponteiro do mouse for posicionado sobre o elemento dá ao navegador o tempo que ele precisa para otimizar o processamento para a modificação. O tempo entre o posicionamento do ponteiro do mouse sobre o elemento e o clique nele é sufuciente para o navegador embora muito rápido para uma reação humana. Isso dará ao navegdor um tempo aproximado de 200ms antes da modoficação acontecer, o que é suficiente para o navegador processar e otimizar as modificações.

.element {
	/* regras de estilo */
	transition: transform 1s ease-out;
}
.element:hover {
	will-change: transform;
}
.element:active {
	transform: rotateY(180deg);
}

Mas, e se a modificação ocorrer no estado hover e não no clique? As regras CSS mostradas anteriormente não serão úteis. Neste caso, devemos encontrar alguma maneira de prever uma ação anterior ao hover. Por exemplo: a ocorrência de um hover sobre um elemento ancestral do elemento a modificar dará o tempo necessário para o navegador. Observe a seguir:

.element {
	transition: opacity .3s linear;
}
/* declare modificações quando o ponteiro do mouse for colocado sobre o elemento ancestral */
.ancestor:hover .element {
	will-change: opacity;
}
/* aplique modificações em hover no elemento */
.element:hover {
	opacity: .5;
}

Convém ressaltar que a ação hover sobre um elemento ancestral não implica necessariamente que haverá uma ação hover em um de seus elementos descendentes. Neste caso devemos encontrar outro momento para definir will-change como por exemplo: quando uma vista se tornar ativa na aplicação ou se o elemento estiver visível na viewport, situações em que as chances de ocorrer o hover (ou outra ação de interação) são maiores.

Remova will-change depois que as modificações estiverem concluidas

Os recursos utilizados pelo navegador para realizar otimizações nativamente, por padrão, são liberados assim que as modificações terminam e colocados a disposição do navegador para novas tarefas. Definir a propriedade will-change para otimizações sobrescreve o comportamento nativo do navegador e os recursos continuam a ser solicitados. Assim, é importante remover a solicitação de otimização tão logo as modificações terminem com o objetivo de liberar o navegador da tarefa de solicitar recursos que não estão sendo mais usados naquelas modificações.

Não se esqueça de remover will-change depois das modificações com a finalidade de liberar os recursos utilizados pelo navegador e ele possa usá-los em outras tarefas.

Não é possível remover a propriedade will-change quando ela for declarada em folhas de estilo, razão pela qual a recomendação é que a definição e remoção da propriedade seja feita com uso de JavaScript. Com JavaScript é possível não somente definir a propriedade como monitorar o término das modificações e então remover will-change. Por exemplo: tal como fizemos com uso de folha de estilo você pode, com JavaScript, "escutar" o momento em que o elemento ou seu ancestral recebe o ponteiro do mouse com uso do evento mouseenter e então definir will-change. Se o elemento for animado você pode "escutar" o término da animação com o evento DOM animationEnd e remover will-change assim que animationEnd disparar.

// Exemplo geral
// Armazena o elemento a ser animado com um clique
var el = document.getElementById('element');

// Define e remove will-change nos eventos mouseenter e animationEnd 
el.addEventListener('mouseenter', hintBrowser);
el.addEventListener('animationEnd', removeHint);

function hintBrowser() {
	// Otimiza propriedades a modificar na animação
	this.style.willChange = 'transform, opacity';
}

function removeHint() {
	this.style.willChange = 'auto';
}

Craig Buckler escreveu uma matéria sobre captura de eventos em animações CSS com uso de JavaScript que eu recomendo a leitura para aqueles que não estão familiarizados com o assunto. Há também uma matéria sobre o controle de animações e transições CSS no site CSS-Tricks, cuja leitura é interessante.

Em folhas de estilo declare will-change com moderação

Como visto anteriormente a propriedade will-change destina-se a informar ao navegador sobre modificações que irão ocorrer alguns milissegundos adiante. Neste caso declarar will-change em uma folha de estilo está OK. Embora a recomendação seja a declarar e remover will-change com uso de JavaScript, em algumas situações declarar em uma folha de estilo e não remover pode ser apropriado.

Um exemplo de ocorrência de uma destas situações é declarar will-change para uma quantidade reduzida de elementos que estarão sujeitos a ação interativa do usuário de forma repetitiva e constante. Quantidade reduzida de elementos é aquela que não sobrecarregue o navegador em demasia. Por exemplo: fazer deslizar uma barra de navegação colocando-a à vista quando o usuário a requisita. A regra CSS mostrada a seguir é perfeitamente apropriada:

.sidebar {
	will-change: transform;
}

Outro exemplo de uso de will-change em folha de estilo é o caso em que um elemento responde à movimentação do ponteiro do mouse na tela. Por ser um comportamento que se repete regular e constantemente deve ser mantido otimizado e a seguinte regra de estilo é apropriada:

.annoying-element-stuck-to-the-mouse-cursor {
	will-change: left, top;
}

Valores da propriedade will-change

Os valores possíveis para a propriedade will-change são: auto, scroll-position, contents, e <custom-ident>.

O valor <custom-ident> define o nome de uma ou mais propriedades que serão modificadas. O exemplo a seguir mostra uma regra CSS válida para declarar will-change para nomes de propriedades:

will-change: transform;
will-change: opacity;
will-change: top, left, bottom, right;

O valor <custom-ident> exclui as palavras-chave will-change, none, all, auto, scroll-position, e contents e mais as palavras-chave normalmente excluidas pela <custom-ident>. Assim, conforme mencionado anteriormente a declaração will-change: all é inválida e será ignorada pelo navegador.

O valor auto não tem um significado particular. Ele informa ao navegador que nenhum otimização especial será processada, a não ser aquelas feitas nativamente.

O valor scroll-position indica, tal como o próprio nome sugere, que a modificação será na posição de rolagem do elemento. Este valor é útil para renderizar conteúdos que serão colocados à vista em um elemento com rolagem. Os navegadores, em, só renderizam conteúdos que estão á vista dentro área de rolagem e nas suas imediações. Definir will-change: scroll-position otimiza a renderização de conteúdos distante da área de visualização.

O valor contents indica que o conteúdo do elemento deverá ser modificado. Os navegadores em geral "cacheiam" conteúdos a serem renderizados. Este valor anula o comportamento de cacheamento do navegador para conteúdos que estão em constante modifição, caso em que manter cachê de conteúdo pode ser inútil e perda de tempo.

Como dito anteriormente a definição de will-change não causa qualquer efeito sobre determinadas propriedades CSS, pois o navegador não realiza qualquer tipo de otimização para elas. É seguro declarar will-change para tais propriedades, pois serão simplesmente ignoradas e não causam qualquer efeito prejudicial. Algumas propriedades podem resultar na criação de contextos de empilhamento (opacity, clip-path, etc.) e/ou contextos de blocos de conteúdos.

Suporte nos navegadores

Quando este artigo foi escrito somente os navegadores Chrome Canary 36+, Opera Developer 23+, e Firefox Nightly suportavam a propriedade will-change. É unânime a intenção de implementar esta funcionalidade. Em breve todos os navegadores modernos suportarão esta propriedade.

Para suporte atual consulte: caniuse (abre em outra janela).

Palavras finais

A propriedade will-change nos auxilia na tarefa de escrever códigos de otimização de performance sem hack e dar ênfase ao ganho de performance em operações CSS. Mas, como tudo o mais, grandes podereres implicam em responsabilidades e will-change é uma das propriedades a ser usada com sabedoria. A seguir cito as palavras de Tab Atkins Jr editor da especificação para will-change.

Defina will-change para as propriedades a modificar nos elementos a modificar e remova a propriedade quando a modificação terminar.

Obrigado por ler!

Muito obrigado ao Paul Lewis pela revisão e feedback a Tab Atkins pelo suporte e respostas, a Bruce Lawson e Mathias Bynens pela revisão do artigo.

Sobre a autora

Sara Soueidan
Crédito: Sara Soueidan é uma desenvolvedora front-end freelance no Líbano. Ela gosta de ensinar e escrever tutoriais no seu blog e no Codrops onde é autora e membro do time. Ela pode ser contactada pelo Twitter e Github.
URL do origial: https://dev.opera.com/articles/css-will-change-property/

Licensed under a Creative Commons Attribution 3.0 Unported license.

X

Matérias recomendadas

A sintaxe da regra CSS

Seletores CSS3