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.
Publicado em 27 de maio de 2020
Quando usadas corretamente, as animações melhoram a percepção e a memória do usuário em relação à 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. Ele foi escrito para servir de base para implementações de animação e transição CSS e permitir o desenvolvimento de efeitos futuros, bem como a composição e a marcação de efeitos já existentes.
Embora o Firefox e o Safari já tenham implementado o conjunto completo de recursos das especificações, o Chromium 84 traz uma série de recursos que não eram compatíveis com o Chrome e o Edge, permitindo a interoperabilidade entre navegadores.
Primeiros passos
A criação de uma animação usando a API Web Animations deve ser bastante familiar se você usar regras @keyframe
. Primeiro, você precisa criar um objeto de keyframe. O resultado será algo como este no CSS:
@keyframes openAnimation {
0% {
transform: scale(0);
}
100% {
transform: scale(1);
}
}
ficaria assim no JavaScript:
const openAnimation = [
{ transform: 'scale(0)' },
{ transform: 'scale(1)' },
];
Em que você define parâmetros para animação no CSS:
.modal {
animation: openAnimation 1s 1 ease-in;
}
você definiria no JS:
document.querySelector('.modal').animate(
openAnimation, {
duration: 1000, // 1s
iterations: 1, // single iteration
easing: 'ease-in' // easing function
}
);
A quantidade de código é mais ou menos a mesma, mas com o JavaScript, você tem alguns superpoderes que não tem com o CSS sozinho. Isso inclui a capacidade de sequenciar efeitos e um maior controle dos estados de jogo.
Além de element.animate()
No entanto, com a atualização, a API Web Animations não está mais restrita a animações criadas usando 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 usando element.animate()
ou regras CSS (animação ou transição CSS). Confira um exemplo:
Primeiro, você "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 transição cruzada. Depois que o cross-fade é concluído, você exclui a cópia.
Como orquestrar animações com promessas
No Chromium 84, agora há dois métodos que podem ser usados com promessas: animation.ready
e animation.finished
.
animation.ready
permite aguardar até que as mudanças pendentes entrem em vigor, ou seja, alternar entre métodos de controle de mídia, como tocar e pausar.animation.finished
fornece um meio de executar código JavaScript personalizado quando uma animação é concluída.
Continuando com nosso exemplo, crie uma cadeia de animação orquestrada com animation.finished
. Aqui, você tem uma transformação vertical (scaleY
), seguida por uma transformação horizontal (scaleX
), seguida por uma mudança de opacidade em um elemento filho:
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 opções diferentes definidas, como velocidade e facilidade.
No CSS, isso seria complicado de recriar, especialmente ao aplicar animações únicas, mas sequenciadas a vários elementos. Você precisa usar um @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 reverter
O que pode abrir, precisa fechar. Felizmente, desde o Chromium 39, a API Web Animations permite que nossas animações sejam reproduzidas, pausadas e revertidas.
Você pode transformar a animação mostrada anteriormente e criar 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.
Você pode criar duas animações em espera de reprodução (openModal
e uma transformação de opacidade inline) e pausar uma delas, atrasando-a até que a outra seja concluída. Você pode usar promessas para esperar que cada uma seja concluída antes de tocar. Por fim, você pode verificar se uma flag está definida e reverter cada animação.
Exemplo: interações dinâmicas com frames-chave parciais
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: ele define um novo local de destino e aciona uma nova animação. A nova posição inicial é inferida da posição atual.
Novas transições podem ser acionadas enquanto as atuais ainda estão em execução. Isso significa que a transição atual é interrompida e uma nova é 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 memória rapidamente e degradar o desempenho. Para resolver esse problema, animações substituíveis foram introduzidas no Chromium 83, permitindo a limpeza automatizada, em que animações finalizadas são sinalizadas como substituíveis e removidas automaticamente se forem substituídas por outra animação finalizada. Veja o exemplo a seguir:
elem.addEventListener('mousemove', evt => {
rectangle.animate(
{ transform: translate(${evt.clientX}px, ${evt.clientY}px) },
{ duration: 500, fill: 'forwards' }
);
});
Sempre que o mouse se move, o navegador recalcula a posição de cada bola no rastro do cometa e cria uma animação para esse novo ponto. Agora o navegador sabe remover animações antigas (ativando a substituição) quando:
- A animação é concluída.
- Há uma ou mais animações mais altas na ordem composta que também foram concluídas.
- As novas animações estão animando as mesmas propriedades.
É possível conferir exatamente quantas animações estão sendo substituídas somando um contador com cada animação removida, usando anim.onremove
para acionar o contador.
Existem alguns métodos adicionais para levar seu controle de animação ainda mais longe:
animation.replaceState()
oferece uma maneira de rastrear se uma animação está ativa, persistente ou removida.animation.commitStyles()
atualiza o estilo de um elemento com base no estilo subjacente e em todas as animações no 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 é possível definir o modo composto das animações, ou seja, elas podem ser aditiva ou acumulativa, além do modo padrão de "substituir". Os modos compostos permitem que os desenvolvedores programem animações distintas e controlem como os efeitos são combinados. Agora há suporte para três modos compostos: 'replace'
(modo padrão), 'add'
e 'accumulate'
.
Ao compor animações, um desenvolvedor pode escrever efeitos curtos e distintos e combiná-los. No exemplo a seguir, aplicamos um frame-chave de rotação e escala a cada caixa, com o único ajuste sendo o modo composto, adicionado como uma opção:
No modo de composição 'replace'
padrão, a animação final substitui a propriedade de transformação e termina em rotate(360deg) scale(1.4)
. Para 'add'
, o elemento combinável 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, confira The CompositeOperation and CompositeOperationOrAuto enumerations (em inglês) na especificação da Web Animations.
Confira este exemplo de elemento de interface:
Aqui, duas animações top
são compostas. A primeira é uma macroanimação, que move o menu suspenso pela altura total do menu como um efeito de deslizamento da parte de cima da página. A segunda, uma microanimação, aplica um pequeno salto quando chega ao fim. 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 ainda mais adições estão por vir. Confira estas especificações futuras para saber mais sobre o que está por vir: