モーション

  • カスタムの幾何学的な表示とスムーズなフェードアウトには、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 メディアクエリを使用して、激しい動きを好まないユーザーに対して激しい動きをオフにします。

  • 一時停止メカニズムを提供する: ユーザーが自動実行されるカルーセル、バナー、その他の永続的なアニメーションを停止できるようにします。
  • デフォルトで静的ビューにする: デフォルトで静的状態にし、ユーザーがモーションを有効にできるようにすることを検討します。
  • フラッシュの制限(1 秒間に 3 回)を超えないようにする: 明暗の急速な点滅は含めないでください。このような影響により、発作が引き起こされることがあります。

animation-duration: 0.01ms; をグローバルに適用しないでください。特定のアニメーションがより不快になる可能性があります。代わりに、2 つの方法のいずれかを使用して、モーション設定をケースバイケースで処理します。@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 を直接使用することをおすすめします。

  • ウェブ アニメーション API(element.animate())のアニメーションには animationName がないため、event.target.getAnimations() をフィルタする際に名前で照合できません。
  • 同じ名前の複数のアニメーションが同じ要素にアタッチされている場合、AnimationEvent の animationName によるフィルタリングは曖昧になります。
    • TransitionEvent の transitionProperty でのフィルタリングは、遷移プロパティごとに 1 つのイベントしかないため機能します。
  • event.target は常に元の DOM 要素を指すため、要素自体で実行されているアニメーションとその擬似要素の 1 つで実行されているアニメーションを区別しません。一方、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)を使用して、サポートを機能検出できます。