O movimento é uma parte essencial de qualquer experiência digital, guiando 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
em uma linha do tempo de 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 dedisplay: none
e na camada superior (a partir do Chrome 117). - A propriedade
overlay
para controlar o comportamento da camada superior durante uma animação (a partir do Chrome 117).
Mostrar 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. Não são necessários novos valores para oferecer suporte a 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 (confira a demonstração na seção de transição). No entanto, as transições não conseguem criar animações mais complexas, como no exemplo a seguir:
.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 card 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, como esta:
.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 de 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.
Transição de animações discretas
Ao contrário de quando você anima propriedades discretas com keyframes, para fazer a transição de propriedades discretas, é necessário 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 popover, use a pseudoclasse :popover-open
.
Um exemplo simples de caixa de diálogo pode ser assim:
/* 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 de baixo para cima na janela de visualização e saia do efeito na parte de cima 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 desfocar um popover
ou dialog
da camada de cima, adicione a propriedade overlay
à 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 vai ficar 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.
Observação sobre as transições de visualização
Se você estiver fazendo mudanças no DOM, como adicionar e remover elementos dele, outra ótima solução para animações suaves é usar 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 do CSS, as transições de visualização vão processar 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: