Animações da Web: elemento.animate() agora está no Chrome 36

Brendan kenny
Brendan Kenny

A animação na Web já era província do JavaScript, mas, com a migração para dispositivos móveis, as animações migraram para CSS para a sintaxe declarativa e as otimizações que os navegadores conseguiram fazer com ela. Com 60 fps em dispositivos móveis sempre seu objetivo, faz sentido nunca sair daquilo que os navegadores sabem como exibir de forma eficiente.

Parece que mais ferramentas tornam as animações orientadas por JavaScript mais eficientes, mas o principal é uma unificação das animações declarativas e imperativas , em que a decisão de como escrever as animações é baseada no código mais claro, não no que é possível em um formato e não no outro.

As Animações da Web respondem a essa chamada, e a primeira parte chega ao Chrome 36 na forma de element.animate(). Essa nova função permite criar uma animação puramente em JavaScript e executá-la com a mesma eficiência que qualquer animação ou transição CSS. Na verdade, a partir do Chrome 34, o mesmo mecanismo de animações da Web conduz todos esses métodos.

A sintaxe é simples, e seus elementos devem ser familiares se você já criou uma transição ou animação CSS:

element.animate([
    {cssProperty: value0},
    {cssProperty: value1},
    {cssProperty: value2},
    //...
], {
    duration: timeInMs,
    iterations: iterationCount,
    delay: delayValue
});

A maior vantagem dessa nova função é a eliminação de vários aros estranhos que tínhamos que pular para conseguir uma animação suave e sem instabilidade.

Por exemplo, no ano passado, para o Siga o Papai Noel, queríamos que a neve caisse continuamente. Decidimos animá-la usando CSS para que fosse feita de maneira tão eficiente.

No entanto, queríamos escolher dinamicamente a posição horizontal da neve com base na tela e nos eventos da cena. Além disso, a altura da queda de neve (a altura da janela do navegador do usuário) não seria conhecida até que os dados fossem executados. Isso significava que realmente tínhamos que usar transições CSS, já que a criação de uma animação CSS no tempo de execução fica complexa rapidamente (e centenas de flocos de neve significam centenas de novas regras de estilo).

Então, adotamos a seguinte abordagem, que deve ser familiar:

snowFlake.style.transform = 'translate(' + snowLeft + 'px, -100%)';
// wait a frame
snowFlake.offsetWidth;
snowFlake.style.transitionProperty = 'transform';
snowFlake.style.transitionDuration = '1500ms';
snowFlake.style.transform = 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)';

A chave está no comentário 'aguardar um frame'. Para iniciar uma transição, o navegador precisa confirmar que o elemento está na posição inicial. Há algumas maneiras de fazer isso. Uma das maneiras mais comuns é ler uma das propriedades do elemento que forçam o navegador a calcular o layout, garantindo que ele saiba que o elemento tem uma posição inicial antes de fazer a transição para a final. O uso desse método permite que você parabenize-se por seu conhecimento superior sobre o funcionamento interno do navegador e ainda se sinta sujo a cada tecla.

Por outro lado, a chamada element.animate() equivalente não poderia ser mais clara, dizendo exatamente o que é esperado:

snowFlake.animate([
    {transform: 'translate(' + snowLeft + 'px, -100%)'},
    {transform: 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)'}
], 1500);

Há muitas outras opções. Assim como acontece com os CSSs, as Animações da Web podem ser atrasadas e iteradas:

snowFlake.animate([
    {transform: 'translate(' + snowLeft + 'px, -100%)'},
    {transform: 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)'}
], {
    duration: 1500,
    iterations: 10,
    delay: 300
});

AnimationPlayer

Na verdade, element.animate() retorna um objeto AnimationPlayer, que se tornará cada vez mais importante à medida que mais especificações de Web Animations forem lançadas. Ambas as animações criadas em JavaScript e CSS terão AnimationPlayers associados, permitindo que sejam combinadas perfeitamente de maneiras úteis e interessantes.

Por enquanto, no entanto, o AnimationPlayer tem apenas duas funcionalidades, ambas muito úteis. É possível cancelar uma animação a qualquer momento usando AnimationPlayer.cancel():

var player = snowFlake.animate([
    {transform: 'translate(' + snowLeft + 'px, -100%)'},
    {transform: 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)'}
], 1500);
// less than 1500ms later...changed my mind
player.cancel();

E, para alívio de quem já tentou criar um sistema de animação em torno de animações ou transições CSS, as animações da Web sempre disparam um evento quando são concluídas:

var player = snowFlake.animate([
    {transform: 'translate(' + snowLeft + 'px, -100%)'},
    {transform: 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)'}
], 1500);
player.onfinish = function(e) {
    console.log('per aspera ad terra!');
}

Testar

Tudo isso está disponível no Chrome 36 e hoje está na versão Beta. Se você quiser testá-lo, tente trabalhar com a implementação nativa no Chrome 36. No entanto, existe um polyfill de Web Animations, que traz uma parte significativamente maior da especificação completa de animações da Web para qualquer um dos navegadores modernos e permanentes.

Uma demonstração do efeito neve está disponível para você testar usando a versão nativa do element.animate() e o polyfill.

Deixe sua opinião

No entanto, essa é uma prévia do que está por vir e está sendo lançada especificamente para receber feedback dos desenvolvedores imediatamente. Ainda não temos certeza se alcançamos todos os casos de uso ou se todos os lados das APIs atuais para animação foram processados. A única maneira de sabermos e acertarmos essa parte é que os desenvolvedores testem o recurso e nos contem o que acharam.

Os comentários nesta postagem são valiosos, e os comentários sobre o padrão em si podem ser enviados para os grupos de trabalho do CSS e do SVG pela lista de e-mails public-fx.

Atualização, outubro de 2014: o Chrome 39 inclui suporte a vários métodos adicionais relacionados ao controle de reprodução, como play(), pause() e reverse(). Ela também oferece suporte para pular para um ponto específico na linha do tempo de uma animação usando a propriedade currentTime. Confira essa funcionalidade em ação nesta nova demonstração.

Agradecemos a Addy Osmani e Max Heinritz pela ajuda nesta postagem.