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

Brendan Kenny
Brendan Kenny

A animação na Web já foi domínio do JavaScript, mas, à medida que o mundo migrou para dispositivos móveis, as animações foram migradas para o CSS para a sintaxe declarativa e as otimizações que os navegadores conseguiram fazer com ele. Como a meta é sempre 60 fps em dispositivos móveis, faz sentido nunca sair do que os navegadores sabem exibir de maneira eficiente.

Várias ferramentas estão surgindo para tornar as animações baseadas em JavaScript mais eficientes, mas o objetivo é a unificação de animações declarativas e imperativas , em que a decisão de como programar as animações é baseada no código mais claro, não no que é possível em uma forma e não em outra.

A Web Animations pode atender a essa chamada, e a primeira parte dela foi lançada no 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 de qualquer animação ou transição CSS. Na verdade, a partir do Chrome 34, o mesmo mecanismo de animações da Web é responsável por todos esses métodos.

A sintaxe é simples, e as partes devem ser familiares se você já tiver criado 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 muitas etapas complicadas que antes eram necessárias para conseguir uma animação suave e sem falhas.

Por exemplo, no Papai Noel do ano passado, queríamos que a neve caísse continuamente e decidimos fazer a animação usando CSS para fazer isso de forma eficiente.

No entanto, queríamos escolher a posição horizontal da neve de forma dinâmica com base na tela e nos eventos que ocorrem na 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 a execução fosse iniciada. Isso significa que precisamos usar as transições CSS, já que criar uma animação CSS no momento da execução fica rapidamente complexo (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 "wait a frame". Para iniciar uma transição, o navegador precisa reconhecer 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ça 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 posição final. Com esse método, você pode se orgulhar do seu conhecimento superior sobre os componentes internos do navegador, mas ainda se sentir mal com cada tecla pressionada.

Em contraste, a chamada element.animate() equivalente não poderia ser mais clara, dizendo exatamente o que se pretende:

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

Há muitas outras opções. Assim como as contrapartes do CSS, as animações da Web podem ser adiadas e iteradas:

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

AnimationPlayer

element.animate() retorna um objeto AnimationPlayer, que vai se tornar cada vez mais importante à medida que mais especificações da Web Animations forem lançadas. As animações criadas em JavaScript e CSS terão AnimationPlayers associados, permitindo que sejam combinadas de forma simples e interessante.

Por enquanto, 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 todos que tentaram criar um sistema de animação com animações ou transições CSS no passado, as animações da Web sempre acionam 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!');
}

Faça um teste

Tudo isso está sendo enviado no Chrome 36, que está sendo lançado hoje na versão Beta. Se quiser testar, tente trabalhar com a implementação nativa no Chrome 36. No entanto, há um polyfill de animações da Web, que traz uma parte significativamente maior da especificação completa de animações da Web para qualquer um dos navegadores modernos e atualizados.

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

Deixe sua opinião

Na verdade, essa é uma prévia do que está por vir, e ela está sendo lançada especificamente para receber feedback dos desenvolvedores imediatamente. Ainda não temos certeza se atendemos a todos os casos de uso ou se nivelamos todas as arestas das APIs atuais para animação. A única maneira de sabermos e fazermos isso direito é pedir que os desenvolvedores testem e nos digam o que acham.

Os comentários sobre esta postagem são, claro, valiosos, e os comentários sobre o padrão podem ser enviados aos grupos de trabalho do CSS e SVG pela lista de discussão public-fx.

Atualização de outubro de 2014: o Chrome 39 adiciona suporte a vários métodos relacionados ao controle de reprodução, como play(), pause() e reverse(). Ele também oferece suporte ao salto 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 com esta postagem.