animation-accessibility / 中文方法笔记

减少运动,保留意义

整理于 2026.10.23 · 11 个小节

来源:animation-accessibility/SKILL.md 与 SNIPPETS.md,含平滑滚动、动态图、视频、循环、React hook、多步骤界面的全部配方主题。

7.1 偏好与两套行为

Prefers-reduced-motion 的 reduce 表示用户希望减少非必要运动,no-preference 表示没有表达该偏好。默认尊重系统,不必再等站点内开启。教程建议普通版与减少运动版同时设计和验收。

“保留意义”不等于必须留淡入。立即呈现文字/状态同样可以清楚;颜色与透明度也不能是唯一的信息来源。

普通版Reduce 版
抽屉滑入、弹窗缩放直接出现或短淡入
多步骤横滑、动态高度静态切换/交叉淡入,取消布局补间
装饰视差、漂浮、循环关闭或停在代表帧
用户拖动保留必要跟手,减少释放惯性与回弹
教学过程动画可手动分步看关键状态

7.2 CSS 分支

把运动覆盖放在组件自身附近,减少共享样式漏网。检查 hover、active、keyframes、嵌套 transform、height、平滑滚动和第三方库,不是仅把 transition-duration 改成 0。

.panel {
  opacity: 1;
  transform: translateY(0);
  transition: opacity 180ms ease, transform 180ms ease-out;
}
@media (prefers-reduced-motion: reduce) {
  .panel { transition: opacity 120ms ease; }
  .panel[data-entering] { transform: none; }
}
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

这是覆盖机制示意;组件真实关闭状态、hidden 与焦点状态要另行定义,不能把 opacity:0 当完整隐藏。

7.3 Motion 的两层保障

MotionConfig reducedMotion="user" 自动关闭后代的 transform/layout 动画,其他值的动画仍可能保留;不是“全站只允许 opacity 和 backgroundColor”的白名单。UseReducedMotion 用于内容分支、媒体、测量高度等额外逻辑。官方说明。

对多步骤界面分别检查:步骤 x 位移、容器 height、按钮行 layout,三处都要调整。只把 y 设零而漏掉 layout,仍会移动。关闭高度动画时还要释放之前写入的固定 height。

7.4 偏好在运行中也会变

自建 hook 应订阅 matchMedia change,并在卸载时清理。SSR 不直接访问 window;首屏采用静态/保守表现,避免偏好尚未读取时自动播放。下面是可用的保守 hook:

import { useEffect, useState } from "react";

export function usePrefersReducedMotion() {
  const [reduce, setReduce] = useState(true);
  useEffect(() => {
    const query = window.matchMedia("(prefers-reduced-motion: reduce)");
    const update = () => setReduce(query.matches);
    update();
    query.addEventListener("change", update);
    return () => query.removeEventListener("change", update);
  }, []);
  return reduce;
}

初始 true 是本教材的保守选择;不能让偏好分支改变关键服务端内容而造成 hydration 不一致。

7.5 动态图片

Picture 的 source 用 no-preference 选择动画资源,img 放静态回退,alt 按图片实际含义编写。静态与动态资源的尺寸应一致,避免切换跳布局。是否下载了哪个资源受实际选择、预加载与缓存影响,不笼统保证“永不下载动画文件”。

7.6 视频

默认有代表性的 poster 和可用 controls。允许自动播放时也要处理 play() Promise 拒绝;reduce 下不自动开始,保留用户主动播放。运行中偏好改为 reduce,按设计停止自主播放,并保持状态标签与真实播放状态一致。原文只切 autoplay 属性的代码是教学起点,不能假设浏览器一定开始播放。

7.7 循环停在哪一帧

Paused 配负 delay 可定位到代表性时间,而不是空白首帧。但循环时间会取模:一个 0.2s 循环的 -0.4s 恰好经过两轮,会回到同一相位,未必是好看的中间帧。先选代表进度,再算时间。暂停与关闭动画的选择也要看循环是否承载信息。

7.8 Hover、焦点和命中

Hover 仅用于有此能力的输入;信息不能只靠 hover,键盘和触摸要可用。扩大视觉小按钮的命中区时检查重叠。原文两次点击模拟 hover 不是所有触屏的默认最佳方案;显式按钮、直接显示信息通常更清楚。

输入控件保留真实 label;伪 placeholder、复制图标和副本标 aria-hidden,不要让辅助技术读到重复内容。不要用颜色、轻微动画作为唯一成功或错误信号。

7.9 业务与动画解耦

业务完成、可交互状态、卸载清理不能只依赖某个 animationend。动画可能被取消、暂停、替换,duration 可能为零,用户可能中途切路由。Presence 回调服务表现生命周期,业务状态需要独立成立。

7.10 验收清单

系统开启 reduce→刷新首屏→切换设置→开关弹层→改变内容高度→拖动与释放→键盘导航→暂停/播放媒体。检查没有遗漏的位移、惯性、平滑滚动,信息仍完整。自动演示首屏暂停、可逐帧,是本课程弹簧演示采用的模式。

自测与答案

  1. Reduce 必须留淡入吗?不必须,关键是信息可理解且无非必要运动。

  2. MotionConfig 会处理视频 autoplay 吗?不会,需要媒体逻辑处理。

  3. 初始 hook 值正确就够了吗?还要订阅运行中变化并考虑 SSR。

  4. 只有 scale 关掉但容器仍变高?测量 height 或 layout 分支漏掉了。

关于这份笔记

来自作者已整理的 Skills 中文自学资料。数字和配方是调试起点,库与浏览器的行为需结合实际版本验证;教材中的原演示验收记录不等同于你的项目已通过测试。

技术校正与版本边界 ↗完整来源说明 ↗
继续下一篇中文术语与反向检索