Melhorias na API Web Animations no Chromium 84

Orquestração de animações com promessas, melhorias de desempenho com animações substituíveis, animações mais suaves com modos compostos e muito mais.

Kevin Ellis
Kevin Ellis

Quando usadas corretamente, as animações melhoram a percepção e a memória do usuário da sua marca, orientam as ações do usuário e ajudam a navegar pelo aplicativo, fornecendo contexto em um espaço digital.

A API Web Animations é uma ferramenta que permite aos desenvolvedores criar animações imperativas com JavaScript. Ela foi escrita para apoiar as implementações de transição e animação CSS e permitir o desenvolvimento de efeitos futuros, bem como os efeitos existentes a serem compostos e cronometrados.

Embora o Firefox e o Safari já tenham implementado o conjunto completo de recursos de especificações, o Chromium 84 traz uma série de recursos anteriormente incompatíveis para o Google Chrome e o Edge, o que permite a interoperabilidade entre navegadores.

A API Web Animations chegou ao Chromium pela primeira vez na versão 36, em julho de 2014. Agora a especificação estará completa, na versão 84, lançada em julho de 2020.
O longo histórico da API Web Animations no Chromium.

Como começar

Se você usou as regras @keyframe, a criação de uma animação pela API Web Animations será bastante familiar. Primeiro você precisa criar um objeto de frame-chave. O que ficaria assim no CSS:

@keyframes openAnimation {
  0% {
    transform: scale(0);
  }
  100% {
    transform: scale(1);
  }
}

ficaria assim em JavaScript:

const openAnimation = [
  { transform: 'scale(0)' },
  { transform: 'scale(1)' },
];

Onde você define parâmetros para animação no CSS:

.modal {
  animation: openAnimation 1s 1 ease-in;
}

você definiria em JS:

document.querySelector('.modal').animate(
    openAnimation, {
      duration: 1000, // 1s
      iterations: 1, // single iteration
      easing: 'ease-in' // easing function
    }
);

A quantidade de códigos é quase a mesma, mas com o JavaScript, você tem alguns superpoderes que não tem apenas com o CSS. Isso inclui a capacidade de sequenciar efeitos e um maior controle dos estados de reprodução.

Além de element.animate()

No entanto, com a atualização, a API Web Animations não está mais restrita a animações criadas por element.animate() Também podemos manipular animações e transições CSS.

getAnimations() é um método que retorna todas as animações em um elemento, independentemente de ele ter sido criado via element.animate() ou usando regras CSS (animação ou transição CSS). Aqui está um exemplo de como isso seria:

Você primeiro "get" os frames-chave da transição para determinar de onde estamos fazendo a transição. Em seguida, você cria duas novas animações de opacidade, ativando o efeito de cross fade. Quando o cross-fade for concluído, você excluirá a cópia.

Orquestração de animações com promessas

No Chromium 84, agora você tem dois métodos que podem ser usados com promessas: animation.ready e animation.finished.

  • animation.ready permite que você aguarde até que as alterações pendentes entrem em vigor, ou seja, alternando entre métodos de controle de mídia, como reproduzir e pausar.
  • O animation.finished fornece um meio de executar o código JavaScript personalizado quando uma animação é concluída.

Vamos continuar com nosso exemplo e criar uma cadeia de animação orquestrada com animation.finished. Aqui, temos uma transformação vertical (scaleY), seguida de uma transformação horizontal (scaleX) e uma mudança de opacidade em um elemento filho:

Aplicar transformações e opacidade a um elemento modal de abertura. Confira a demonstração no Codepen
const transformAnimation = modal.animate(openModal, openModalSettings);
transformAnimation.finished.then(() => { text.animate(fadeIn, fadeInSettings)});

Encadeamos essas animações usando animation.finished.then() antes de executar o próximo conjunto de animações na cadeia. Dessa forma, as animações aparecem em ordem e você aplica efeitos a diferentes elementos de destino com diferentes opções definidas (como velocidade e facilidade).

No CSS, isso seria complicado de recriar, especialmente ao aplicar animações exclusivas, mas sequenciadas, a vários elementos. Será necessário usar uma @keyframe, classificar as porcentagens de tempo corretas para posicionar as animações e usar animation-delay antes de acionar as animações na sequência.

Exemplo: reproduzir, pausar e inverter

O que pode abrir, deve fechar. Felizmente, desde o Chromium 39, a API Web Animations nos forneceu a capacidade de reproduzir, pausar e reverter nossas animações.

Você pode usar a animação acima e conceder a ela uma animação suave e invertida ao clicar no botão novamente usando .reverse(). Dessa forma, você pode criar uma interação mais suave e contextual para nosso modal.

Exemplo de abertura e fechamento modal com o clique de um botão. Confira a demonstração do Glitch

Você pode criar duas animações com reprodução pendente (openModal e uma transformação de opacidade in-line) e pausar uma delas, atrasando até que a outra seja concluída. Em seguida, use promessas para esperar que cada uma seja concluída antes de jogar. Por fim, confira se há um flag definido e inverta cada animação.

Exemplo: interações dinâmicas com frames-chave parciais

Exemplo de nova segmentação, em que um clique do mouse ajusta a animação para um novo local. Confira a demonstração do Glitch
selector.animate([{transform: `translate(${x}px, ${y}px)`}],
    {duration: 1000, fill: 'forwards'});

Neste exemplo, há apenas um frame-chave e nenhuma posição inicial especificada. Este é um exemplo de uso de frames-chave parciais. O manipulador do mouse faz algumas coisas aqui: define um novo local de término e aciona uma nova animação. A nova posição inicial é inferida da posição subjacente atual.

Novas transições podem ser acionadas enquanto as existentes ainda estão em execução. Isso significa que a transição atual será interrompida e uma nova será criada.

Melhorias de desempenho com animações substituíveis

Ao criar animações com base em eventos, como em 'mousemove', uma nova animação é criada a cada vez, o que pode consumir rapidamente a memória e prejudicar o desempenho. Para resolver esse problema, animações substituíveis foram introduzidas no Chromium 83, permitindo a limpeza automatizada, em que as animações concluídas são sinalizadas como substituíveis e removidas automaticamente se substituídas por outra animação concluída. Confira o exemplo abaixo:

Uma trilha de cometa é animada quando o mouse se move. Confira a demonstração do Glitch
elem.addEventListener('mousemove', evt => {
  rectangle.animate(
    { transform: translate(${evt.clientX}px, ${evt.clientY}px) },
    { duration: 500, fill: 'forwards' }
  );
});

Cada vez que o mouse se move, o navegador recalcula a posição de cada bola na trilha do cometa e cria uma animação para esse novo ponto. O navegador agora sabe remover animações antigas (ativando a substituição) quando:

  1. A animação foi concluída.
  2. Há uma ou mais animações em uma ordem composta que também são concluídas.
  3. As novas animações estão animando as mesmas propriedades.

É possível conferir exatamente quantas animações estão sendo substituídas computando um contador com cada animação removida, usando anim.onremove para acionar o contador.

Há alguns outros métodos para aumentar ainda mais o controle da animação:

  • O animation.replaceState() fornece um meio de rastrear se uma animação está ativa, persistida ou removida.
  • animation.commitStyles() atualiza o estilo de um elemento com base no estilo subjacente, além de todas as animações do elemento na ordem composta.
  • animation.persist() marca uma animação como não substituível.

Animações mais suaves com modos compostos

Com a API Web Animations, agora você pode definir o modo composto das animações, o que significa que elas podem ser cumulativas ou cumulativas, além do modo padrão de "substituição". Os modos compostos permitem que os desenvolvedores escrevam animações distintas e tenham controle sobre como os efeitos são combinados. Três modos compostos agora podem ser usados: 'replace' (o modo padrão), 'add' e 'accumulate'.

Quando você compõe animações, um desenvolvedor pode escrever efeitos curtos e distintos e vê-los combinados. No exemplo abaixo, estamos aplicando um frame-chave de rotação e escala a cada caixa, com o único ajuste sendo o modo composto, adicionado como opção:

Uma demonstração mostrando os modos compostos padrão, adição e acumulação. Confira a demonstração do Glitch

No modo composto 'replace' padrão, a animação final substitui a propriedade de transformação e termina em rotate(360deg) scale(1.4). Para 'add', o composto adiciona a rotação e multiplica a escala, resultando em um estado final de rotate(720deg) scale(1.96). 'accumulate' combina as transformações, resultando em rotate(720deg) scale(1.8). Para saber mais sobre as complexidades desses modos compostos, consulte Enumerações CompositeOperation e CompositeOperationOrAuto (em inglês) na especificação Web Animations.

Veja um exemplo de elemento da interface:

Um menu suspenso saltitante com duas animações compostas aplicadas a ele. Confira a demonstração do Glitch

Aqui, duas animações top são compostas. O primeiro é uma macroanimação, que move o menu suspenso pela altura total do próprio menu como um efeito de deslizamento do topo da página, e a segunda, uma microanimação, aplica um pouco de saliência ao atingir a parte inferior. O uso do modo composto 'add' permite uma transição mais suave.

const dropDown = menu.animate(
    [
      { top: `${-menuHeight}px`, easing: 'ease-in' },
      { top: 0 }
    ], { duration: 300, fill: 'forwards' });

  dropDown.finished.then(() => {
    const bounce = menu.animate(
      [
        { top: '0px', easing: 'ease-in' },
        { top: '10px', easing: 'ease-out' },
        { ... }
      ], { duration: 300, composite: 'add' });
  });

Próximas etapas para a API Web Animations

Essas são adições incríveis aos recursos de animação nos navegadores atuais, e outras adições estão chegando. Confira essas especificações futuras para uma leitura mais aprofundada sobre o que vem a seguir: