Quatro novos recursos CSS para facilitar a entrada e saída de animações

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 e content-visibility em uma linha do tempo de frame-chave (a partir do Chrome 116).
  • A propriedade transition-behavior com a palavra-chave allow-discrete para ativar transições de propriedades discretas, como display (a partir do Chrome 117).
  • A regra @starting-style para animar os efeitos de entrada de display: 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. Elas serão trocadas 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 propriedades discretas

As propriedades que são animadas de forma discreta não acionam eventos de transição por padrão. Para ativar isso, defina o modo de comportamento de transição como allow-discrete.

A propriedade transition-behavior

A propriedade transition-behavior especifica se as transições serão iniciadas ou não para propriedades discretas. Ele aceita dois valores: normal e allow-discrete, sendo o valor inicial normal.

  • normal: as transições não são iniciadas para propriedades discretas, apenas para propriedades interpoláveis.
  • allow-discrete: as transições serão iniciadas para propriedades discretas e interpoláveis.

Para ativar o modo allow-discrete para uma propriedade específica, inclua-a na abreviação transition:

.card {   transition: opacity 0.25s, display 0.25s allow-discrete; /* Enable allow-discrete for the display property */ }  .card.fade-out {   opacity: 0;   display: none; } 
Observação: essa demonstração de transição mostra uma técnica diferente da primeira demonstração de animação, mas parece visualmente semelhante.

Ao fazer a transição de várias propriedades discretas, é necessário definir allow-discrete para cada propriedade em transição. Exemplo:

.card {   transition: opacity 0.5s, display 0.5s allow-discrete, overlay 0.5s allow-discrete; } 

Como alternativa, para definir o comportamento de todas as propriedades de transição, declare transition-behavior: allow-discrete após a declaração transition. Essa é geralmente a abordagem mais fácil.

.card {   transition: opacity 0.5s, display 0.5s, overlay 0.5s;   transition-behavior: allow-discrete; /* Note: be sure to write this after the shorthand */ } 

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 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: