Controle de reprodução de animações da Web no Chrome 39

No início deste ano, o Chrome 36 lançou o método element.animate como parte da especificação Web Animations (link em inglês) mais abrangente. Isso permite animações nativas eficientes que são escritas de forma imperativa, oferecendo aos desenvolvedores a opção de criar animações e transições com a abordagem mais adequada.

Para relembrar, veja como você pode animar uma nuvem pela tela, com um callback quando terminar:

var player = cloud.animate([
    {transform: 'translateX(' + start + 'px)'},
    {transform: 'translateX(' + end + 'px)'}
], 5000);
player.onfinish = function() {
    console.info('Cloud moved across the screen!');
    startRaining(cloud);
};

Por si só, isso é incrivelmente fácil e vale a pena considerar como parte de sua caixa de ferramentas ao criar animações ou transições de forma imperativa. Entretanto, no Chrome 39, os recursos de controle de mídia foram adicionados ao objeto AnimationPlayer retornado por element.animate. Antes, depois que uma animação era criada, você só podia chamar cancel() ou ouvir o evento de conclusão.

Essas adições de reprodução abrem as possibilidades do que as animações da Web podem fazer, ou seja, transformar animações em uma ferramenta de uso geral, em vez de restringir transições, ou seja, Animações "fixas" ou predefinidas.

Pausar, retroceder ou mudar a velocidade de reprodução

Vamos começar atualizando o exemplo acima para pausar a animação se alguém clicar na nuvem:

cloud.addEventListener('mousedown', function() {
    player.pause();
});

Também é possível modificar a propriedade playbackRate:

function changeWindSpeed() {
    player.playbackRate *= (Math.random() * 2.0);
}

Você também pode chamar o método reverse(), que normalmente é equivalente a inverter a playbackRate atual (multiplicar por -1). No entanto, há alguns casos especiais:

  • Se a mudança causada pelo método reverse() fizer com que a animação em execução termine efetivamente, o currentTime também será invertido.Por exemplo, se uma nova animação for invertida, toda a animação será reproduzida na ordem inversa.

  • Se o player estiver pausado, a animação começará a ser reproduzida.

Como arrastar o marcador no player

Uma AnimationPlayer agora permite que o currentTime seja modificado enquanto uma animação está em execução Normalmente, esse valor vai aumentar com o tempo (ou diminuir, se o playbackRate for negativo). Isso pode permitir que a posição de uma animação seja controlada externamente, talvez por meio da interação do usuário. Isso é chamado de refinamento.

Por exemplo, se sua página HTML representava o céu e você gostaria de um gesto de arrastar para alterar a posição de uma nuvem em reprodução, você poderia adicionar alguns gerenciadores ao documento:

var startEvent, startEventTime;
document.addEventListener('touchstart', function(event) {
    startEvent = event;
    startEventTime = players.currentTime;
    player.pause();
});
document.addEventListener('touchmove', function(event) {
    if (!startEvent) return;
    var delta = startEvent.touches[0].screenX -
        event.changedTouches[0].screenX;
    player.currentTime = startEventTime + delta;
});

Conforme você arrasta o documento, a currentTime será alterada para refletir a distância do evento original. Também é possível retomar a animação quando o gesto terminar:

document.addEventListener('touchend', function(event) {
    startEvent = null;
    player.play();
});

Isso pode até ser combinado com o comportamento de inversão, dependendo de onde o mouse foi retirado da página (demonstração combinada).

Em vez de refinar um AnimationPlayer em resposta a uma interação do usuário, o currentTime também pode ser usado para mostrar o progresso ou o status, por exemplo, para mostrar o status de um download.

A utilidade aqui é que uma AnimationPlayer permite que um valor seja definido e faça com que a implementação nativa subjacente faça a visualização do progresso. No caso de download, a duração de uma animação pode ser definida como o tamanho total do download, e a currentTime pode ser definida como o tamanho salvo no momento (demonstração).

Transições e gestos da interface

Há muito tempo, as plataformas móveis são o reino dos gestos comuns: arrastar, deslizar, deslizar e similares. Esses gestos tendem a ter um tema comum: um componente de interface arrastável, como o "puxar para atualizar" da visualização em lista ou uma barra lateral sendo criada do lado esquerdo da tela.

Com as animações na Web, é muito fácil replicar um efeito semelhante na Web, no computador ou no dispositivo móvel. Por exemplo, quando um gesto que controla currentTime é concluído:

var steps = [ /* animation steps */ ];
var duration = 1000;
var player = target.animate(steps, duration);
player.pause();
configureStartMoveListeners(player);

var setpoints = [0, 500, 1000];
document.addEventListener('touchend', function(event) {
    var srcTime = player.currentTime;
    var dstTime = findNearest(setpoints, srcTime);
    var driftDuration = dstTime - srcTime;

    if (!driftDuration) {
    runCallback(dstTime);
    return;
    }

    var driftPlayer = target.animate(steps, {
    duration: duration,
    iterationStart: Math.min(srcTime, dstTime) / duration,
    iterations: Math.abs(driftDuration) / duration,
    playbackRate: Math.sign(driftDuration)
    });
    driftPlayer.onfinish = function() { runCallback(dstTime); };
    player.currentTime = dstTime;
});

Isso cria uma animação adicional que executa um "deslocamento". É feita entre o ponto em que o gesto foi concluído e o destino conhecido.

Isso funciona, já que as animações têm prioridade com base na ordem de criação: neste caso, driftPlayer terá precedência sobre o player. Quando driftPlayer for concluído, ele e os efeitos dele desaparecerão. No entanto, o tempo final vai corresponder ao currentTime do jogador subjacente, então a interface vai permanecer consistente.

Por fim, se você gosta de gatinhos, há um aplicativo de demonstração da Web (link em inglês) que mostra esses gestos. Ele é compatível com dispositivos móveis e usa o polyfill para oferecer compatibilidade com versões anteriores. Portanto, tente carregá-lo no seu dispositivo móvel.

Vá em frente e element.animate

O método element.animate é totalmente incrível por enquanto, seja para animações simples ou para usar o AnimationPlayer retornado de outras maneiras.

Esses dois recursos também são totalmente compatíveis com outros navegadores modernos por meio de um polyfill leve. Esse polyfill também detecta recursos. Assim, à medida que os fornecedores de navegadores implementam a especificação, esse recurso só vai ficar mais rápido e melhor com o tempo.

A especificação de animações da Web também continuará a evoluir. Se você tiver interesse em testar os próximos recursos, eles também estão disponíveis agora em um polyfill mais detalhado: web-animations-next.