O movimento é uma parte essencial de qualquer experiência digital, orientando o usuário de uma interação para a próxima. Mas existem algumas lacunas nas animações suaves na plataforma web. Eles incluem a capacidade de animar facilmente as animações de entrada e saída, além da animação suave de e para a camada superior em elementos dispensáveis, como caixas de diálogo e pop-overs.
Para preencher essas lacunas, o Chrome 116 e 117 incluem quatro novos recursos da plataforma 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 as transições de propriedades discretas, comodisplay
(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, é possível usar display
e content-visibility
nas regras de frame-chave. Eles serão alterados no momento em que o frame-chave 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 0,5 segundo e, em seguida, 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 ao qual ela foi aplicada 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 gatilho para as animações. 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
. Há muitos casos em que convém ir além e remover o nó DOM com um tempo limite para permitir que a animação termine primeiro.
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 possibilita transições discretas 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. Assim, 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 distintas, precisará incluir allow-discrete
depois de cada uma delas. 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, é necessário 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. Este é o estado "antes de abrir" (de onde você está animando).
/* 0. BEFORE-OPEN STATE */
/* Starting point for the transition */
@starting-style {
.item {
opacity: 0;
height: 0;
}
}
/* 1. 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;
}
/* 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:
Animar elementos de e para a camada superior
Para animar elementos da camada superior e a partir dela, especifique o @starting-style
no estado "aberto" para informar ao navegador de onde a animação será iniciada. 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. BEFORE-OPEN STATE */
@starting-style {
dialog[open] {
translate: 0 100vh;
}
}
/* 1. IS-OPEN STATE */
dialog[open] {
translate: 0 0;
}
/* 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 um encapsulamento mais visual.
Ao animar um pop-over, use a pseudoclasse :popover-open
em vez do atributo open
usado anteriormente.
.settings-popover {
&:popover-open {
/* 0. 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;
}
/* 1. IS-OPEN STATE */
/* state when popover is open, BOTH:
what we're transitioning *in* to
and transitioning *out* from */
transform: translateY(0);
opacity: 1;
}
/* 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
fazem o escape de clipes e transformações ancestrais e também colocam o conteúdo na camada superior. Se você não fizer a transição de overlay
, seu elemento voltará imediatamente a ser recortado, transformado e coberto, e a transição não vai acontecer.
[open] {
transition: opacity 1s, display 1s allow-discrete;
}
Em vez disso, inclua overlay
na transição ou animação para animar a overlay
com o restante dos recursos e garantir que ela 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 há 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-over ao fazer a transição, ele primeiro será movido para fora da camada superior, pulando para trás do outro pop-over, antes de iniciar a transição. Esse efeito não é 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. Aqui estão 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 da seguinte forma:
Primeiro, no CSS, atribua um view-transition-name
individual a cada card.
.card-1 {
view-transition-name: card-1;
}
.card-2 {
view-transition-name: card-2;
}
/* etc. */
Em seguida, em JavaScript, una a mutação do DOM (nesse caso, removendo o cartão) 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 processa o esmaecimento e a transformação de cada card na nova posição.
Outro exemplo de onde isso pode ser útil é na demonstração de como adicionar/remover itens da 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 ainda mais de animações de entrada e saída suaves na plataforma da Web. Para mais detalhes, confira estes links: