모션

  • clip-path 및 mask-image를 사용하여 맞춤 기하학적 표시 및 부드러운 페이드 아웃을 구현합니다.
  • JS 리스너 대신 스크롤 기반 애니메이션 (animation-timeline: scroll())을 사용하여 필수적이지 않은 스크롤 기반 효과를 구현합니다.
  • 뷰 전환을 사용하여 복잡한 레이아웃 상태 간에 원활하게 애니메이션을 적용합니다.

성능

  • 애니메이션이 컴포지터 스레드에 유지되도록 opacity 및 transform (개별 변환 속성 포함, 예: left/right/top/bottom 대신 translate)에 애니메이션을 적용하는 것이 좋습니다.
  • 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 미디어 쿼리를 사용하여 모션이 많은 것을 선호하지 않는 사용자를 위해 모션을 사용 중지합니다.

  • 일시중지 메커니즘 제공: 사용자가 자동 실행 캐러셀, 배너 또는 기타 지속적인 애니메이션을 중지할 수 있도록 합니다.
  • 정적 뷰를 기본값으로 설정: 정적 상태를 기본값으로 설정하고 사용자가 모션을 선택할 수 있도록 하는 것이 좋습니다.
  • 깜박임 한도 (초당 3회)를 초과하지 않기: 밝은 빛과 어두운 빛이 빠르게 깜박이는 콘텐츠를 포함하지 않습니다. 이러한 효과는 발작을 일으킬 수 있습니다.

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를 사용할 수 없는 경우 일반적으로 event.target.getAnimations()를 호출하고 event.animationName (또는 전환의 경우 event.propertyName)로 필터링하여 Animation 인스턴스를 찾을 수 있습니다. 하지만 event.target.getAnimations()를 필터링하는 이 대체가 예상대로 작동하지 않는 여러 시나리오를 방지하려면 event.animation를 직접 사용하는 것이 좋습니다.

  • Web Animations API (element.animate()) 애니메이션에는 animationName가 없으므로 event.target.getAnimations()를 필터링할 때 이름으로 일치시킬 수 없습니다.
  • 이름이 같은 여러 애니메이션이 동일한 요소에 연결된 경우 AnimationEvent의 animationName로 필터링하면 모호해집니다.
    • TransitionEvent의 transitionProperty에 대한 필터링은 전환 속성당 하나의 이벤트만 있으므로 작동합니다.
  • 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)를 사용하여 지원을 기능 감지할 수 있습니다.