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 mais ampla de animações da Web. Isso permite animações nativas eficientes escritas de forma imperativa, aos desenvolvedores a opção de criar animações e transições com a abordagem mais adequada para eles.

Para relembrar, veja como animar uma nuvem na 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);
};

Isso por si só é incrivelmente fácil e vale a pena considerar como parte da sua caixa de ferramentas ao criar animações ou transições de forma imperativa. No entanto, no Chrome 39, os recursos de controle de reprodução foram adicionados ao objeto AnimationPlayer retornado por element.animate. Antes, depois que uma animação era criada, só era possível chamar cancel() ou detectar o evento de conclusão.

Essas adições de reprodução ampliam as possibilidades do que as animações da Web podem fazer, transformando-as em uma ferramenta de uso geral, em vez de prescrever transições, por exemplo, "fixadas" ou predefinidas.

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

Vamos começar atualizando o exemplo acima para pausar a animação se a nuvem for clicada:

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

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

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

Também é possível chamar o método reverse(), que normalmente equivale a inverter o 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 seja encerrada, o currentTime também será invertido.Por exemplo, se uma animação nova for invertida, toda a animação será reproduzida ao contrário.

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

Como usar o player

Um AnimationPlayer agora permite que o currentTime seja modificado enquanto uma animação está em execução. Normalmente, esse valor aumenta com o tempo (ou diminui, se o playbackRate for negativo). Isso pode permitir que a posição de uma animação seja controlada externamente, talvez pela interação do usuário. Isso é comumente chamado de limpeza.

Por exemplo, se a página HTML representasse o céu e você quisesse que um gesto de arrasto mudasse a posição de uma nuvem em reprodução, seria possível adicionar alguns manipuladores 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;
});

À medida que você arrasta o documento, o currentTime é alterado para refletir a distância do evento original. Você também pode retomar a exibição da animação quando o gesto terminar:

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

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

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

O utilitário aqui é que um AnimationPlayer permite que um valor seja definido e que a implementação nativa subjacente cuide da visualização do progresso. No caso do download, a duração de uma animação pode ser definida como o tamanho total do download, e o currentTime pode ser definido como o tamanho do download atual (demonstração).

Transições e gestos da interface

As plataformas móveis sempre foram o reino dos gestos comuns: arrastar, deslizar, deslizar rapidamente e assim por diante. Esses gestos tendem a ter um tema comum: um componente de interface arrastável, como o "puxe para atualizar" de uma visualização de lista ou uma barra lateral que aparece no lado esquerdo da tela.

Com as animações da Web, é muito fácil replicar um efeito semelhante na Web, em computadores ou dispositivos móveis. 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 extra que executa uma "deriva". Isso acontece entre o local onde o gesto foi concluído e o destino conhecido.

Isso funciona porque as animações têm uma prioridade com base na ordem de criação. Neste caso, driftPlayer tem precedência sobre o jogador. Quando driftPlayer for concluído, ele e os efeitos dele vão desaparecer. No entanto, o tempo final vai corresponder ao currentTime do jogador, para que a interface continue consistente.

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

Vá em frente e use element.animate

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

Esses dois recursos também têm suporte total em outros navegadores modernos por meio de um polyfill leve. Esse polyfill também realiza a detecção de recursos. Portanto, à 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 vai continuar evoluindo. Se você quiser testar os próximos recursos, eles também estão disponíveis em um polyfill mais detalhado: web-animations-next.