运动模式

  • 使用 clip-path 和 mask-image 可实现自定义几何图形显现和流畅淡出。
  • 使用滚动驱动的动画 (animation-timeline: scroll()) 实现非必要的滚动绑定效果,而不是使用 JS 监听器。
  • 使用视图过渡在复杂的布局状态之间无缝添加动画效果。

性能

  • 最好为 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 媒体查询为偏好减少动画的用户关闭剧烈动画。

  • 提供暂停机制:允许用户停止自动运行的轮播界面、横幅或其他持续动画。
  • 默认采用静态视图:考虑默认采用静态状态,并允许用户选择启用动态效果。
  • 不要超出闪烁频率限制(每秒 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.animation,以避免出现以下几种情况:过滤 event.target.getAnimations() 的后备机制无法按预期运行:

  • Web Animations API (element.animate()) 动画没有 animationName,因此在过滤 event.target.getAnimations() 时无法按名称进行匹配。
  • 当多个同名动画附加到同一元素时,按 animationName 过滤 AnimationEvent 会产生歧义。
    • 请注意,对 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)检测功能支持情况。