Movimento

  • Use clip-path e mask-image para revelações geométricas personalizadas e esmaecimentos suaves.
  • Use Animações orientadas por rolagem (animation-timeline: scroll()) para efeitos de rolagem não essenciais em vez de listeners JS.
  • Use as transições de visualização para animar entre estados de layout complexos de forma integrada.

Desempenho

  • Dê preferência à animação de opacity e transform (incluindo propriedades de transformação individuais, como translate em vez de left/right/top/bottom) para garantir que as animações permaneçam na linha de execução do compositor.
  • Use transition-behavior: allow-discrete, @starting-style e (para elementos de camada superior, como <dialog> ou [popover]) overlay para animar estados de entrada e saída discretos de forma nativa. Consulte Animar a entrada e saída de elementos e Animar elementos de e para a camada superior.
.popover-reveal {
  /* Transition discrete display and top-layer overlay alongside opacity */
  transition:
    opacity ease-out,
    display,
    overlay;
  transition-duration: 0.2s;
  transition-behavior: allow-discrete;
}

Acessibilidade

Use consultas de mídia prefers-reduced-motion para desativar movimentos intensos para os usuários que preferem isso.

  • Ofereça um mecanismo de pausa: permita que os usuários interrompam carrosséis, banners ou outras animações persistentes de execução automática.
  • Use visualizações estáticas como padrão: considere usar estados estáticos como padrão e permitir que os usuários ativem o movimento.
  • Não exceda os limites de flash (três por segundo): nunca inclua flashes rápidos de luz para escuro. Esses efeitos podem causar convulsões.

NÃO aplique animation-duration: 0.01ms; globalmente, porque isso pode fazer com que algumas animações fiquem mais bruscas. Em vez disso, processe as preferências de movimento caso a caso usando uma das duas abordagens, aninhando a consulta @media dentro da regra para que as duas versões sejam mantidas juntas:

  • Comece com as animações ativadas por padrão e reduza-as usando @media (prefers-reduced-motion: reduce):
/* Good: Dampen spin states or slow down animations for reduced motion */
.spinner {
  animation: spin 1s linear infinite;
  @media (prefers-reduced-motion: reduce) {
    animation: none;
    opacity: 0.5;
  }
}

progress:not([value]) {
  animation: slide 1s infinite linear;
  @media (prefers-reduced-motion: reduce) {
    animation-duration: 20s;
  }
}
  • Comece sem animações por padrão e ative-as usando @media (prefers-reduced-motion: no-preference):
/* Good: Default to static or gentle states and opt-in to full motion */
.spinner {
  opacity: 0.5;
  @media (prefers-reduced-motion: no-preference) {
    opacity: 1;
    animation: spin 1s linear infinite;
  }
}

progress:not([value]) {
  animation: slide 20s infinite linear;
  @media (prefers-reduced-motion: no-preference) {
    animation-duration: 1s;
  }
}

Como trabalhar com eventos de animação e transição em JavaScript

Nas interfaces AnimationEvent (animationstart, animationiteration, animationend, animationcancel) e TransitionEvent (transitionrun, transitionstart, transitionend, transitioncancel), event.target fornece o elemento DOM de origem. Para acessar o objeto Animation específico (Animation, CSSAnimation ou CSSTransition) que acionou o evento, use a propriedade somente leitura event.animation.

Quando event.animation não estiver disponível, geralmente é possível encontrar a instância Animation chamando event.target.getAnimations() e filtrando por event.animationName (ou event.propertyName para transições). No entanto, é preferível usar event.animation diretamente para evitar vários cenários em que esse substituto que filtra event.target.getAnimations() não funciona como esperado:

  • As animações da API Web Animations (element.animate()) não têm um animationName, então não podem ser correspondidas por nome ao filtrar event.target.getAnimations().
  • Filtrar por animationName para AnimationEvents é ambíguo quando várias animações com o mesmo nome são anexadas ao mesmo elemento.
    • A filtragem em transitionProperty para TransitionEvents funciona porque há apenas um evento por propriedade de transição.
  • event.target sempre aponta para o elemento DOM de origem, portanto, não distingue entre animações em execução no próprio elemento e em um dos pseudoelementos, enquanto event.animation.effect.pseudoElement expõe o pseudoelemento de destino diretamente.
// Works in most basic CSS animation cases, but fails for WAAPI animations,
// duplicate animation names on the same element, or pseudo-elements.
document.addEventListener('animationstart', (event) => {
  const animation = event.target
    .getAnimations()
    .find((anim) => anim.animationName === event.animationName);
  console.log(animation);
});

// PREFERRED: Directly access the exact Animation object via event.animation
document.addEventListener('animationstart', (event) => {
  const animation = event.animation;
  console.log(animation);
});

É possível detectar o suporte a recursos usando 'animation' in AnimationEvent.prototype (ou 'animation' in TransitionEvent.prototype).