Ruch

  • Używaj clip-path i mask-image do niestandardowych geometrycznych efektów odkrywania i płynnego zanikania.
  • Zamiast detektorów JS używaj animacji sterowanych przewijaniem (animation-timeline: scroll()) do nieistotnych efektów związanych z przewijaniem.
  • Używaj przejść widoku, aby płynnie animować przejścia między złożonymi stanami układu.

Wydajność

  • Zalecamy animowanie właściwości opacity i transform (w tym poszczególnych właściwości przekształcania, np. translate zamiast left/right/top/bottom), aby animacje pozostały w wątku kompozytora.
  • Używaj transition-behavior: allow-discrete, @starting-style i (w przypadku elementów najwyższej warstwy, takich jak <dialog> lub [popover]) overlay, aby natywnie animować dyskretne stany wejścia i wyjścia. Zobacz Animowanie wejścia i wyjścia elementu oraz Animowanie elementów do i z najwyższej warstwy.
.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;
}

Ułatwienia dostępu

Użyj zapytań o media prefers-reduced-motion, aby wyłączyć intensywny ruch dla użytkowników, którzy wolą go nie widzieć.

  • Udostępnij mechanizm wstrzymywania: pozwól użytkownikom zatrzymywać automatycznie uruchamiane karuzele, banery i inne trwałe animacje.
  • Domyślne widoki statyczne: rozważ ustawienie domyślnych stanów statycznych i umożliwienie użytkownikom włączenia ruchu.
  • Nie przekraczaj limitów migania (3 razy na sekundę): nigdy nie używaj szybkiego migania światła i ciemności. Takie efekty mogą powodować napady padaczkowe.

NIE stosuj animation-duration: 0.01ms; globalnie, ponieważ może to spowodować, że niektóre animacje będą bardziej uciążliwe. Zamiast tego obsługuj preferencje dotyczące ruchu w poszczególnych przypadkach, korzystając z jednego z dwóch podejść, zagnieżdżając zapytanie @media w regule, aby obie wersje były przechowywane razem:

  • Zacznij od domyślnie włączonych animacji i ogranicz je za pomocą @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;
  }
}
  • Domyślnie nie używaj animacji i włącz je za pomocą @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;
  }
}

Praca ze zdarzeniami animacji i przejść w języku JavaScript

W interfejsach AnimationEvent (animationstart, animationiteration, animationend, animationcancel) i TransitionEvent (transitionrun, transitionstart, transitionend, transitioncancel) event.target podaje element DOM, z którego pochodzi zdarzenie. Aby uzyskać dostęp do konkretnego obiektu Animation (Animation, CSSAnimation lub CSSTransition), który wywołał zdarzenie, użyj właściwości tylko do odczytu event.animation.

Gdy event.animation jest niedostępny, zwykle możesz znaleźć instancję Animation, wywołując event.target.getAnimations() i filtrując według event.animationName (lub event.propertyName w przypadku przejść). Zalecamy jednak bezpośrednie używanie event.animation, aby uniknąć sytuacji, w których ta kreacja zastępcza filtrująca event.target.getAnimations() nie działa zgodnie z oczekiwaniami:

  • Animacje Web Animations API (element.animate()) nie mają właściwości animationName, więc nie można ich dopasować według nazwy podczas filtrowania event.target.getAnimations().
  • Filtrowanie według animationName dla AnimationEvent jest niejednoznaczne, gdy do tego samego elementu jest dołączonych wiele animacji o tej samej nazwie.
    • Pamiętaj, że filtrowanie według transitionProperty dla TransitionEvent działa, ponieważ dla każdej właściwości przejścia jest tylko jedno zdarzenie.
  • event.target zawsze wskazuje element DOM, z którego pochodzi, więc nie rozróżnia animacji uruchamianych na samym elemencie od animacji uruchamianych na jednym z jego pseudoelementów, podczas gdy event.animation.effect.pseudoElement bezpośrednio ujawnia docelowy pseudoelement.
// 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);
});

Możesz wykryć obsługę funkcji za pomocą 'animation' in AnimationEvent.prototype (lub 'animation' in TransitionEvent.prototype).