Motion System v4.2 是一套面向 React / Next.js 的生产级 UI 动效系统,定位是性能、可访问性与可用性,而不是装饰。
用途与判断标准
该系统的核心原则是:动效必须引导注意力(如引导流程、关键操作)、传达状态(加载、成功、错误、过渡)、保持空间连续性(布局变化、导航)。如果动效做不到这三点,就应移除。适合使用的场景包括交互组件(按钮、模态框、菜单)、状态过渡(加载→已加载、打开→关闭)、导航与布局连续性(共享元素、交叉淡入淡出)。不适合使用动效的情况包括:纯装饰、降低可用性或清晰度、对性能产生负面影响。
安装与版本
安装命令为 npm install motion。版本方面,motion/react 是当前 Motion for React 项目的默认导入路径(包名 motion);framer-motion 是仍依赖 Framer Motion 的项目的旧版导入路径。两者不可混用,混用会导致内部调度器冲突和 AnimatePresence 上下文损坏,来自一个包的组件无法与另一个包的组件协调退出动画。可用 cat package.json | grep -E '"motion"|"framer-motion"' 检查项目使用哪个版本,并始终从同一来源导入。
动效令牌
系统提供 motionTokens.ts,包含 duration(fast 0.18、normal 0.35、slow 0.6)、easing(smooth [0.22, 1, 0.36, 1]、sharp [0.4, 0, 0.2, 1])和 distance(sm 8、md 16、lg 24)。这些令牌用于 transition 对象中的 duration 与 ease 值。
性能规则与设备适配
安全属性为 transform 和 opacity;应避免 width / height 和 top / left。规则是响应性优先于平滑度。设备适配启发式结合 CPU 核心数与可用内存:deviceMemory 在 Chrome/Android 可用,其他环境回退到 hardwareConcurrency。当 deviceMemory ≤ 2,或 deviceMemory 未定义且 hardwareConcurrency ≤ 4 时判定为低端设备,时长相应缩短(示例中低端 0.2,否则 0.4)。
无障碍
必须始终支持减弱动效。JS 侧使用 useReducedMotion,根据 reduce 决定初始位移与时长;CSS 侧使用 @media (prefers-reduced-motion: reduce) 覆盖 transition 与 transform;Tailwind 侧使用 motion-safe 与 motion-reduce 变体。
架构与模式
核心模式对应关系:悬停反馈用 whileHover,点按反馈用 whileTap,滚动进入视口用 whileInView,滚动联动值用 useScroll + useTransform,条件挂载/卸载用 AnimatePresence,小范围布局变化(单元素、约 300px 以内)用 layout 属性,大范围布局变化或整页重排应避免 layout,改用 CSS 过渡或页面级路由;复杂命令式序列用 useAnimate。避免在大容器上使用 layout 的原因是布局动画通过 transform 协调位置,但全视口元素或触发深层重排时测量成本会造成可见卡顿与 CLS,应优先使用 CSS Grid/Flexbox 过渡,或仅在特定子元素上配合 layoutId。
布局与过渡
共享元素过渡使用 layoutId(每个已挂载实例必须唯一);进入/退出过渡使用 AnimatePresence。AnimatePresence 的 mode 必须显式指定,默认 "sync" 会同时运行进入与退出,在多数 UI 模式中造成视觉重叠。"wait" 用于退出完成后再进入,适合模态框、toast、页面过渡;"sync" 仅在重叠是有意设计时使用(如交叉淡入淡出轮播);"popLayout" 用于退出元素立即脱离文档流、其余项动画填充,适合列表、标签页、可关闭卡片。
高级模式(概念)
包括视差(滚动联动变换)、滚动叙事(粘性区块)、3D 倾斜(基于指针的变换)、交叉淡入淡出(共享 layoutId)、渐进显示(clip-path)、骨架加载(循环透明度)、微交互(悬停/点按反馈)、弹簧系统(基于物理的动效)。
模态框要点
需要焦点陷阱、Escape 关闭、滚动锁定、ARIA 角色,并使用 AnimatePresence mode="wait" 确保退出动画完成后下一个模态框才进入。
SSR 安全
服务端与客户端渲染的初始状态要匹配;避免隐式动画起点(始终显式设置 initial);在 Next.js App Router 中用 "use client" 包裹动效组件。
调试与 QA
调试检查项:导入错误(混用 motion/react 与 framer-motion)、Next.js App Router 缺少 "use client"、AnimatePresence 子元素缺少 key、水合不匹配(SSR 与客户端初始状态不同)、在大容器上误用 layout 导致重排卡顿、状态驱动动画未触发(检查依赖数组)。QA 清单:无 CLS、键盘可用、模态框焦点被捕获、ARIA 角色正确(role="dialog"、aria-modal="true")、尊重减弱动效(useReducedMotion + CSS 媒体查询)、Next.js 无水合警告、卸载时动画干净停止(无内存泄漏)、所有使用处显式设置 AnimatePresence mode。
反模式
动画化布局属性(width、height、top、left);无目的的无限动画(始终问:它传达什么状态?);列表过度交错(staggerChildren ≤ 0.1s,超过会显得慢);忽略减弱动效偏好;在大容器或全视口容器上使用 layout;省略 AnimatePresence 的 mode(默认 "sync" 造成视觉重叠);纯粹为装饰使用动效。
代码示例
资料提供了按钮交互(whileHover scale 1.02、whileTap scale 0.97)、减弱动效(useReducedMotion 控制 y 与时长)、交错列表(staggerChildren 0.08)、模态框(AnimatePresence mode="wait")、滚动视差(useScroll + useTransform)、骨架加载(opacity 循环,duration 1.5、repeat Infinity、easeInOut)、共享布局(layoutId="shared")等完整示例。
理念与最终规则
动效即交互设计。最终规则:如果动效不能改善用户体验,就移除它。