O movimento é uma parte essencial de qualquer experiência digital, orientando o usuário de uma interação para a próxima. No entanto, há algumas lacunas nas animações suaves na plataforma da Web. Isso inclui a capacidade de animar facilmente animações de entrada e saída e animar suavemente para e da camada superior para elementos dispensáveis, como caixas de diálogo e pop-ups.
Para preencher essas lacunas, o Chrome 116 e o 117 incluem quatro novos recursos da plataforma da Web, que permitem animações e transições suaves para propriedades discretas.
Esses quatro novos recursos incluem:
- A capacidade de animar
display
econtent-visibility
na linha do tempo de um frame-chave (a partir do Chrome 116). - A propriedade
transition-behavior
com a palavra-chaveallow-discrete
para ativar transições de propriedades discretas, comodisplay
(a partir do Chrome 117). - A regra
@starting-style
para animar os efeitos de entrada dodisplay: none
para a camada superior (do Chrome 117). - A propriedade
overlay
para controlar o comportamento da camada superior durante uma animação (a partir do Chrome 117).
Exibir animações em frames-chave
No Chrome 116 e versões mais recentes, é possível usar display
e content-visibility
nas regras de keyframe. Eles serão trocados no momento em que o keyframe ocorrer. Nenhum novo valor adicional é necessário para suportar isso:
.card {
animation: fade-out 0.5s forwards;
}
@keyframes fade-out {
100% {
opacity: 0;
display: none;
}
}
O exemplo anterior anima a opacidade para 0 durante a duração de 0,5s e define a exibição como "nenhuma". Além disso, a palavra-chave forwards
garante que a animação permaneça no estado final, para que o elemento aplicado permaneça display: none
e opacity: 0
.
Este é um exemplo simples que imita o que você pode fazer com uma transição (veja a demonstração na seção de transição). No entanto, as transições não podem criar animações mais complexas, como no exemplo abaixo:
.card {
animation: spin-and-delete 1s ease-in forwards;
}
@keyframes spin-and-delete {
0% {
transform: rotateY(0);
filter: hue-rotate(0);
}
80% {
transform: rotateY(360deg);
filter: hue-rotate(180deg);
opacity: 1;
}
100% {
opacity: 0;
display: none;
}
}
A animação spin-and-delete
é uma animação de saída. Primeiro, o card gira no eixo y, passa por uma rotação de matiz e, em 80%
na linha do tempo, faz a transição da opacidade de 1 para 0. Por fim, o cartão muda de display: block
para display: none
.
Para essas animações de saída, em vez de aplicá-las diretamente a um elemento, você pode configurar um acionador para elas. Por exemplo, anexando um listener de eventos a um botão que aciona uma classe para aplicar a animação, da seguinte forma:
.spin-out {
animation: spin-and-delete 1s ease-in forwards;
}
document.querySelector('.delete-btn').addEventListener('click', () => {
document.querySelector('.card').classList.add('spin-out');
})
O exemplo acima agora tem um estado final display:none
. Em muitos casos, é necessário ir além e remover o nó DOM com um tempo limite para permitir que a animação seja concluída primeiro.
Como fazer a transição de animações discretas
Ao contrário da animação de propriedades distintas com frames-chave, para fazer a transição de propriedades discretas, você vai precisar usar o modo de comportamento de transição allow-discrete
.
A propriedade transition-behavior
O modo allow-discrete
é o que torna as transições discretas possíveis e é um valor da propriedade transition-behavior
. transition-behavior
aceita dois valores: normal
e allow-discrete
.
.card {
transition: opacity 0.25s, display 0.25s;
transition-behavior: allow-discrete; /* Note: be sure to write this after the shorthand */
}
.card.fade-out {
opacity: 0;
display: none;
}
A abreviação transition
também define esse valor. Portanto, você pode omitir a propriedade e usar a palavra-chave allow-discrete
no final da abreviação transition
para cada transição.
.card {
transition: opacity 0.5s, display 0.5s allow-discrete;
}
.card.fade-out {
opacity: 0;
display: none;
}
Se você estiver animando várias propriedades discretas, será necessário incluir allow-discrete
após cada propriedade que quiser animar. Exemplo:
.card {
transition: opacity 0.5s, display 0.5s allow-discrete, overlay 0.5s allow-discrete;
}
.card.fade-out {
opacity: 0;
display: none;
}
A regra @starting-style
para animações de entrada
Até agora, este artigo abordou animações de saída. Para criar animações de entrada, você precisa usar a regra @starting-style
.
Use @starting-style
para aplicar um estilo que o navegador possa procurar antes que o elemento seja aberto na página. Esse é o estado "antes da abertura" (de onde você está animando).
/* 0. IS-OPEN STATE */
/* The state at which the element is open + transition logic */
.item {
height: 3rem;
display: grid;
overflow: hidden;
transition: opacity 0.5s, transform 0.5s, height 0.5s, display 0.5s allow-discrete;
}
/* 1. BEFORE-OPEN STATE */
/* Starting point for the transition */
@starting-style {
.item {
opacity: 0;
height: 0;
}
}
/* 2. EXITING STATE */
/* While it is deleting, before DOM removal in JS, apply this
transformation for height, opacity, and a transform which
skews the element and moves it to the left before setting
it to display: none */
.is-deleting {
opacity: 0;
height: 0;
display: none;
transform: skewX(50deg) translateX(-25vw);
}
Agora você tem um estado de entrada e saída para esses itens da lista de tarefas:
Como animar elementos para e da camada superior
Para animar elementos de e para a camada superior, especifique o @starting-style
no estado "aberto" para informar ao navegador de onde a animação vai começar. Para uma caixa de diálogo, o estado aberto é definido com o atributo [open]
. Para um pop-over, use a pseudoclasse :popover-open
.
Este é um exemplo simples de caixa de diálogo:
/* 0. IS-OPEN STATE */
dialog[open] {
translate: 0 0;
}
/* 1. BEFORE-OPEN STATE */
@starting-style {
dialog[open] {
translate: 0 100vh;
}
}
/* 2. EXIT STATE */
dialog {
transition: translate 0.7s ease-out, overlay 0.7s ease-out allow-discrete, display 0.7s ease-out allow-discrete;
translate: 0 100vh;
}
No próximo exemplo, os efeitos de entrada e saída são diferentes. Entre animando a partir da parte inferior da janela de visualização e saia do efeito para a parte superior da janela de visualização. Ele também é escrito com CSS aninhado para mais encapsulamento visual.
Ao animar um pop-up, use a pseudoclasse :popover-open
em vez do atributo open
usado anteriormente.
.settings-popover {
&:popover-open {
/* 0. IS-OPEN STATE */
/* state when popover is open, BOTH:
what we're transitioning *in* to
and transitioning *out* from */
transform: translateY(0);
opacity: 1;
/* 1. BEFORE-OPEN STATE */
/* Initial state for what we're animating *in* from,
in this case: goes from lower (y + 20px) to center */
@starting-style {
transform: translateY(20px);
opacity: 0;
}
}
/* 2. EXIT STATE */
/* Initial state for what we're animating *out* to ,
in this case: goes from center to (y - 50px) higher */
transform: translateY(-50px);
opacity: 0;
/* Enumerate transitioning properties,
including display and allow-discrete mode */
transition: transform 0.5s, opacity 0.5s, display 0.5s allow-discrete;
}
Propriedade overlay
Por fim, para esmaecer uma popover
ou dialog
da camada superior, adicione a propriedade overlay
à sua lista de transições. popover
e dialog
escapam de clipes e transformações ancestrais e também colocam o conteúdo na camada superior. Se você não fizer a transição overlay
, o elemento vai voltar imediatamente a ser cortado, transformado e coberto, e você não vai notar a transição.
[open] {
transition: opacity 1s, display 1s allow-discrete;
}
Em vez disso, inclua overlay
na transição ou animação para animar overlay
com o restante dos recursos e garantir que ele permaneça na camada superior durante a animação. Isso será muito mais suave.
[open] {
transition: opacity 1s, display 1s allow-discrete, overlay 1s allow-discrete;
}
Além disso, quando você tem vários elementos abertos na camada superior, a sobreposição ajuda a controlar a transição suave para dentro e para fora da camada superior. Confira a diferença neste exemplo simples. Se você não aplicar overlay
ao segundo pop-up durante a transição, ele vai sair da camada de cima, pulando para trás do outro pop-up, antes de iniciar a transição. Esse não é um efeito muito suave.
Uma observação sobre as transições de visualização
Se você estiver fazendo alterações no DOM, como adicionar e remover elementos do DOM, outra ótima solução para ter animações suaves são as transições de visualização. Confira dois dos exemplos acima criados usando transições de visualização.
Nesta primeira demonstração, em vez de configurar @starting-style
e outras transformações CSS, as transições de visualização vão fazer a transição. A transição de visualização é configurada assim:
Primeiro, no CSS, atribua a cada cartão um view-transition-name
individual.
.card-1 {
view-transition-name: card-1;
}
.card-2 {
view-transition-name: card-2;
}
/* etc. */
Em seguida, no JavaScript, envolva a mutação do DOM (neste caso, removendo o card) em uma transição de visualização.
deleteBtn.addEventListener('click', () => {
// Check for browser support
if (document.startViewTransition) {
document.startViewTransition(() => {
// DOM mutation
card.remove();
});
}
// Alternative if no browser support
else {
card.remove();
}
})
Agora, o navegador pode processar o desbotamento e a transformação de cada card para a nova posição.
Outro exemplo em que isso pode ser útil é na demonstração de adição/remoção de itens de lista. Nesse caso, você precisa adicionar um view-transition-name
exclusivo para cada cartão criado.
Conclusão
Esses novos recursos da plataforma nos aproximam de animações de entrada e saída suaves na plataforma da Web. Para mais detalhes, confira estes links: