Анимация

  • Используйте clip-path и mask-image для создания собственных геометрических эффектов появления и плавного затухания.
  • Используйте анимации, управляемые прокруткой (animation-timeline: scroll()), для второстепенных эффектов, связанных с прокруткой, вместо прослушивателей JS.
  • Используйте View Transitions, чтобы плавно анимировать переходы между сложными макетами.

Эффективность

  • Рекомендуется анимировать opacity и transform (включая отдельные свойства преобразования, например translate вместо left/right/top/bottom), чтобы анимация оставалась в потоке компоновщика.
  • Используйте transition-behavior: allow-discrete, @starting-style и (для элементов верхнего слоя, таких как <dialog> или [popover]) overlay, чтобы анимировать отдельные состояния входа и выхода. Подробнее об анимации входа и выхода элементов и об анимации элементов верхнего слоя…
.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;
}

Специальные возможности

Используйте медиазапросы prefers-reduced-motion, чтобы отключить интенсивное движение для пользователей, которые этого хотят.

  • Добавьте возможность приостановить анимацию. Позвольте пользователям останавливать автоматически запускаемые карусели, баннеры и другие виды анимации.
  • Используйте статичные изображения по умолчанию. Подумайте о том, чтобы по умолчанию использовать статичные изображения и позволить пользователям включать анимацию.
  • Не превышайте ограничения на мерцание (три раза в секунду). Не используйте быстрое мерцание от светлого к темному. Такие эффекты могут вызвать приступы.

НЕ ПРИМЕНЯЙТЕ animation-duration: 0.01ms; глобально, так как это может сделать некоторые анимации более резкими. Вместо этого обрабатывайте настройки движения в каждом конкретном случае, используя один из двух подходов, вкладывая запрос @media в правило, чтобы обе версии сохранялись вместе:

  • Включите анимацию по умолчанию и уменьшите ее интенсивность с помощью @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;
  }
}
  • По умолчанию анимация отключена. Чтобы включить ее, используйте метод @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;
  }
}

Как работать с событиями анимации и перехода в JavaScript

В интерфейсах AnimationEvent (animationstart, animationiteration, animationend, animationcancel) и TransitionEvent (transitionrun, transitionstart, transitionend, transitioncancel) event.target возвращает исходный элемент DOM. Чтобы получить доступ к определенному объекту Animation (Animation, CSSAnimation или CSSTransition), который активировал событие, используйте свойство event.animation, доступное только для чтения.

Если event.animation недоступен, обычно можно найти экземпляр Animation, вызвав event.target.getAnimations() и отфильтровав по event.animationName (или event.propertyName для переходов). Однако лучше использовать event.animation напрямую, чтобы избежать ситуаций, когда резервный вариант, фильтрующий event.target.getAnimations(), работает некорректно:

  • У анимаций Web Animations API (element.animate()) нет animationName, поэтому их нельзя сопоставить по названию при фильтрации event.target.getAnimations().
  • Фильтрация по animationName для AnimationEvent неоднозначна, когда к одному элементу прикреплено несколько анимаций с одинаковым названием.
    • Обратите внимание, что фильтрация по transitionProperty для TransitionEvent работает, поскольку для каждого свойства перехода существует только одно событие.
  • event.target всегда указывает на исходный элемент DOM, поэтому не различает анимации, запущенные на самом элементе и на одном из его псевдоэлементов, тогда как event.animation.effect.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);
});

Вы можете определить поддержку функции с помощью 'animation' in AnimationEvent.prototype (или 'animation' in TransitionEvent.prototype).