- Używaj
clip-pathimask-imagedo 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
opacityitransform(w tym poszczególnych właściwości przekształcania, np.translatezamiastleft/right/top/bottom), aby animacje pozostały w wątku kompozytora. - Używaj
transition-behavior: allow-discrete,@starting-stylei (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ścianimationName, więc nie można ich dopasować według nazwy podczas filtrowaniaevent.target.getAnimations(). - Filtrowanie według
animationNamedlaAnimationEventjest niejednoznaczne, gdy do tego samego elementu jest dołączonych wiele animacji o tej samej nazwie.- Pamiętaj, że filtrowanie według
transitionPropertydlaTransitionEventdziała, ponieważ dla każdej właściwości przejścia jest tylko jedno zdarzenie.
- Pamiętaj, że filtrowanie według
event.targetzawsze 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 gdyevent.animation.effect.pseudoElementbezpoś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).