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

Joey arhar
Joey Arhar

Os movimentos são uma parte essencial de qualquer experiência digital e orientam o usuário de uma interação a outra. No entanto, existem algumas lacunas nas animações suaves na plataforma Web. Isso inclui a possibilidade de animar com facilidade animações de entrada e saída, além de animações suaves de e para a camada superior para elementos dispensáveis, como caixas de diálogo e pop-ups.

Para preencher essas lacunas, o Chrome 116 e 117 incluem quatro novos recursos da plataforma da Web, que permitem animações e transições suaves para propriedades distintas.

Esses quatro novos recursos incluem:

  • Capacidade de animar display e content-visibility em uma linha do tempo de frame-chave (do Chrome 116 em diante).
  • A propriedade transition-behavior com a palavra-chave allow-discrete para ativar transições de propriedades discretas como display (do Chrome 117)
  • A regra @starting-style para animar os efeitos de entrada de display: none para a camada superior (do Chrome 117).
  • A propriedade overlay para controlar o comportamento da camada superior durante uma animação (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 alternados no momento em que o frame-chave ocorrer. Nenhum valor novo é necessário para dar suporte a esse código:

.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,5 s 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 em que ela é 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 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 é de saída. Primeiro, o card gira no eixo y, passa por uma rotação de matiz e, em 80%, na linha do tempo, muda a opacidade de 1 para 0. Por fim, o card alterna 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, desta 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 de 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.

Como fazer a transição de animações discretas

Diferente da animação de propriedades discretas com frames-chave, para fazer a transição de propriedades discretas, você precisa usar o modo de comportamento de transição allow-discrete.

A propriedade transition-behavior

O modo allow-discrete 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;
}
.
Observação: esta demonstração de transição mostra uma técnica diferente da primeira, mas visualmente semelhante.

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 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, é necessário usar a regra @starting-style.

Use @starting-style para aplicar um estilo que o navegador pode consultar antes que o elemento seja aberto na página. Este é o estado "antes de abrir" (de onde você está animando a imagem).

/*  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 de saída para estes itens da lista de tarefas:

Animar elementos de e para a camada superior

Para animar elementos de e para a camada superior, especifique o @starting-style no estado "aberto" para informar ao navegador onde a animação será feita. Em uma caixa de diálogo, o estado aberto é definido com o atributo [open]. Para um pop-up, use a pseudoclasse :popover-open.

Um exemplo simples de caixa de diálogo pode ser assim:

/*   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, saia do efeito para cima da janela de visualização. Ele também é escrito com CSS aninhado para maior encapsulamento visual.

Ao animar um pop-up, 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 será feita.

[open] {
  transition: opacity 1s, display 1s allow-discrete;
}

Em vez disso, inclua overlay na transição ou na 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 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. Veja a diferença neste exemplo simples. Se você não estiver aplicando overlay ao segundo pop-up ao fazer a transição, ele primeiro sairá da camada superior, ficando atrás do outro pop-up antes de iniciar a transição. Esse efeito não é muito suave.

Observação sobre transições de visualização

Se você estiver fazendo mudanças no DOM, como adicionar e remover elementos, outra ótima solução para animações suaves são as transições de visualização. Confira dois dos exemplos acima criados com 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 processar a transição. A transição de visualização é configurada da seguinte forma:

No CSS, primeiro 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, envolva a mutação do DOM (neste 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 pode lidar com o esmaecimento e a transformação de cada cartão para a nova posição.

.

Outro exemplo de onde isso pode ser útil é com a demonstração de adição/remoção de itens de lista. Nesse caso, é preciso adicionar um view-transition-name exclusivo para cada cartão criado.

.

Conclusão

Esses novos recursos da plataforma nos aproximam um pouco mais das animações de entrada e saída suaves na plataforma da Web. Para saber mais, confira estes links: