关于 motion-react
motion-react 是面向 React 开发者的 Motion UI 动画技能,涵盖组件动画、手势和布局过渡。它强调减少动态效果、性能与克制,并帮助判断何时改用 CSS 或 AutoAnimate。
主要功能
- 01组件进入与退出动画
- 02变体编排与布局过渡
- 03手势状态与数值动画
- 04减少动态效果的无障碍支持
- 05在 Motion、CSS 与 AutoAnimate 间选择
适用场景
- 01为列表添加或移除项目时制作动画
- 02实现页面切换或共享元素移动
- 03为计数器和手势交互添加反馈
- 04检查动画的无障碍性与性能
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ bm629/ agent-skills --skill motion-react安装后在 Claude Code 中输入/motion-react确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/a11y-and-restraint.mdintegration-and-testing.mdlayout-and-shared-elements.mdsources.mdvalues-and-imperative.md详细介绍
motion-react — 面向 React 的 Motion
概述
在 React 中使用 Motion 实现 UI 动画。该库此前以 framer-motion 的名称发布,自 2024 年末起独立运营,并以 npm 包 motion 发布(从 "motion/react" 导入)。此技能涵盖 DOM 产品 UI 动画功能:声明式进入/退出、变体编排、布局和共享元素过渡、Motion 值和数字滚动器,以及手势状态,并贯穿三个不可妥协的原则:无障碍的减少动态效果支持、适合合成器的性能和克制(有目的、时长短且一致的动态效果——尤其适用于数据密集型工具)。它还负责判断“我真的需要 Motion 吗?”:许多任务用纯 CSS 或一行 AutoAnimate 调用就能更好地完成。
何时启用
- ✅ 为 React SPA 添加进入/退出/列表动画、页面或视图过渡、共享元素移动、动画计数器或手势反馈。
- ✅ 迁移或编写引用
framer-motion(旧包)的代码——此技能介绍当前的motion用法。 - ✅ 接入减少动态效果支持,或审查现有动画的无障碍性/性能。
- ✅ 为特定交互选择 Motion、CSS/Tailwind 过渡或 AutoAnimate。
以下情况请勿启用:
- 任务涉及动画插画 / Lottie 文件——那属于图像内容,而非 UI 动态效果(由专门的插画技能负责)。
- 任务涉及视频渲染(Remotion)或滚动叙事/视差营销页面——这是不同的类型;此处仅介绍
whileInView基础知识。 - 正在决定产品的动态设计语言(特定应用采用哪些时长/缓动)——这是设计系统的决策;此技能提供相关术语和约束。
工作流
步骤 1:选择合适的工具——大多数动画都不需要 Motion
| 任务 | 选择 | 原因 |
|---|---|---|
| 悬停/聚焦时的颜色、不透明度、小幅缩放 | 纯 CSS / Tailwind transition-* | 无 JS、无包体积开销、适合 GPU |
| 基于 Radix 的组件(手风琴、弹出框、对话框)的打开/关闭样式 | 根据 Radix data-state 属性编写 CSS | 基础组件已提供状态;用 CSS 为其添加动画 |
| 内部工具中的列表添加 / 移除 / 重新排序 | AutoAnimate——在父级上设置一个 useAutoAnimate() ref | 零配置,可为子项的添加/移除/移动添加动画,并自动遵循 prefers-reduced-motion |
卸载时的退出动画、编排序列、共享元素移动、弹簧、数字滚动器、拖动 / 拖动重新排序(Reorder 组件) | Motion | 需要 CSS 无法表达的 JS 生命周期控制 |
| 浏览器原生视图变换(同文档内或跨文档/MPA) | View Transitions API | 列出以供了解;对于 React SPA 中的视图/路由切换,AnimatePresence 可实现同等效果(整份文档的变换,例如主题交叉淡入淡出,则仅能通过 VTA 实现) |
只有在更简单的方案确实无法胜任时,才逐级尝试表格中的下一项。
步骤 2:安装并设定应用级默认策略
npm install motion
import { motion, MotionConfig } from "motion/react"
// App root — respect the user's OS reduced-motion setting everywhere:
<MotionConfig reducedMotion="user">
<App />
</MotionConfig>
- 包名为
motion;React 入口为"motion/react"。支持 React 18+(文档注明 18.2;peer 依赖范围为^18.0.0 || ^19.0.0)。任何导入framer-motion的代码都使用了重命名前的包——它们属于同一系列,但新项目应使用motion, 当前文档和惯用写法也都以该名称为准。 MotionConfig reducedMotion接受"user"(遵循操作系统设置)、"always"(强制减少;用于调试)和"never"(默认值)。启用减少动画后,Motion 会禁用变换和布局动画, 同时保留透明度和颜色动画——通常正是理想的降级方式。在根节点设置"user"作为默认策略;完整约定请参阅references/a11y-and-restraint.md。- 精简打包的接线方式只需一个包装器:
<LazyMotion features={domAnimation} strict><m.div animate={{ opacity: 1 }} /></LazyMotion>。 - React Server Component 框架:
motion组件是客户端组件——添加"use client"边界(或从motion/react-client导入);纯 Vite SPA 则两者都不需要。 - 对包体积敏感的应用:
LazyMotion与m组件(import * as m from "motion/react-m")初始渲染约为 4.6 kB(完整motion约为 34 kB)。可同步加载功能(domAnimation),也可在 hydration 后异步加载,以实现最大幅度的缩减;如果混入完整的motion组件,LazyMotion上的strict会抛出异常。
步骤 3:核心动画
<motion.div
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.2, ease: "easeOut" }}
/>
initial→animate会在挂载时运行;exit仅在卸载时运行,且必须位于AnimatePresence内部(步骤 5)。transition用于选择动画模型:type: "tween"(时长 + 缓动——UI 默认值)或type: "spring"(物理效果;用于手势/拖拽,也可谨慎用于入场动画)。关键帧: 传入数组(animate={{ x: [0, 100, 0] }});首个关键帧为null表示 “当前值”。- 手势状态:
whileHover、whileTap、whileFocus、whileDrag、whileInView—— 手势结束时会恢复原值。仅用于悬停样式时,优先使用 CSS(步骤 1);当手势必须与其他动画值协调时再使用这些属性。 - 产品 UI 的动画时长:微交互为 150–300 ms,较大的过渡不超过 400 ms。时长更长会让人感觉迟滞,而非精致。
步骤 4:Variants——编排父子元素
import { motion, stagger } from "motion/react"
const list = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: { when: "beforeChildren", delayChildren: stagger(0.05) },
},
}
const item = { hidden: { opacity: 0, y: 4 }, visible: { opacity: 1, y: 0 } }
<motion.ul variants={list} initial="hidden" animate="visible">
{rows.map(r => <motion.li key={r.id} variants={item} />)}
</motion.ul>
- Variant 名称会向下传递:父元素状态变化时,带有
variants的子元素也会执行动画——无需逐层传递 props。 - 编排由父元素的
transition控制:when: "beforeChildren" | "afterChildren",以及**delayChildren: stagger(interval)**——这是当前的惯用写法, 自motion12.22.0 起可用(staggerChildren/staggerDirection已在 12.21.0 弃用)。在较旧版本(framer-motion、早期 12.x)中,staggerChildren: 0.05仍然有效,而delayChildren中使用stagger()则不受支持(当时该选项 只能是数字)——迁移这种写法前请先升级包。 - 动态 variants:variant 可以是
custom的函数(例如按索引设置延迟)。
第 5 步:AnimatePresence——退出动画
import { AnimatePresence, motion } from "motion/react"
<AnimatePresence mode="popLayout">
{rows.map(r => (
<motion.div key={r.id} exit={{ opacity: 0, x: -8 }} layout />
))}
</AnimatePresence>
- 通过监视其直接子元素来工作;每个子元素都需要一个稳定且唯一的
key(数组索引会破坏退出动画——这是典型问题)。 mode:"sync"(默认——进入和退出同时进行)、"wait"(完全退出后再进入;一次处理一个子元素——适合视图/路由切换)、"popLayout"(退出的元素从布局流中移除,使相邻元素重新排列——适合列表;父元素需要设置非static的position)。- 原地内容切换(骨架屏→内容、标签面板):使用
"wait"时,两个子元素的尺寸应保持一致,否则会闪现间隙;尺寸不同时,可通过"popLayout"或网格堆叠/绝对定位叠加层实现原地交叉淡化——如果希望尺寸变化本身也有动画效果而非突然跳变,则容器还应搭配layout。 initial={false}会跳过首次渲染时已有子元素的挂载动画。 所有退出动画完成后会触发onExitComplete。- 退出中的组件无法接收属性更新——要实现方向感知的退出动画(轮播、分页视图),请通过 AnimatePresence 的
custom属性传递方向,并在子元素中使用usePresenceData(或变体custom)读取。 - AnimatePresence 必须包裹条件判断——绝不能放在条件判断内部。
第 6 步:超越核心功能——加载与任务匹配的参考资料
- 布局动画、
layoutId共享元素、LayoutGroup、失真修复 →references/layout-and-shared-elements.md - 动效值、
useTransform/useSpring、命令式animate()/useAnimate、统计卡片的动画数字滚动器 →references/values-and-imperative.md - 完整的减少动效规范、WCAG 对应关系、“减少或保留”表格、动效约束令牌 →
references/a11y-and-restraint.md - Radix/shadcn 集成(
forceMount)、Tailwind 边界、路由器页面过渡、AutoAnimate 完整示例,以及测试(MotionGlobalConfig.skipAnimations、Playwright 减少动效)→references/integration-and-testing.md
规则
硬性规则(绝不违反):
- 尊重减少动态效果偏好。 每个使用 Motion 的应用都必须在根节点设置
MotionConfig reducedMotion="user"(或采用明确的等效策略)。绝不能发布会忽略操作系统偏好的大幅位移、缩放、视差或自动播放的动态效果(WCAG 2.3.3;根据 WCAG 2.2.2,自动播放超过 5 s 的动态效果需要提供暂停控件)。 - 对
transform和opacity做动画,不要对会触发重新布局的属性做动画。 绝不要直接对width/height/top/left/边距做动画——请使用layout属性(通过变换实现动画)或缩放;不会触发布局的轻量属性(颜色/背景、SVG 描边值)可以使用。基于缩放的填充效果(进度条、仪表)需要显式设置变换原点(左侧锚定的填充使用originX: 0)——默认值为中心。**唯一允许的例外:**展开/收起内容(手风琴、折叠面板)可使用height: 0 ↔ "auto"配合overflow: hidden制作动画——缩放会扭曲文本,layout也无法实现裁切揭示;动画应简短且由用户触发。请注意,减少动态效果偏好不会自动减少此类动画(只有 transform/layout 动画会被禁用)——如果揭示内容会挤开大量内容,请使用useReducedMotion(),改为直接展开。 - **在 AnimatePresence 内使用稳定的键。**将数组索引作为键会悄无声息地破坏退出动画。
- **从
"motion/react"导入。**不要在新项目中添加framer-motion;不要混用这两个包。 - **Motion 动画必须有明确目的。**动画应传达状态变化、空间关系或反馈——产品 UI 中纯装饰性的动画属于缺陷,而非润色。
偏好(可覆盖):
- 微交互为 150–300 ms;路由/页面切换 ≤ 200 ms;其他视图级过渡 ≤ 400 ms(这是上限,不是目标)。
- UI 状态变化使用补间动画;由手势驱动/模拟物理效果的交互使用弹簧动画。
- 将时长/缓动集中设为设计令牌,不要到处散落字面值。
- 优先选择足够简单的方案(第 1 步),再考虑使用 Motion。
注意事项
- **退出动画始终不播放:**元素不是
AnimatePresence的直接子元素、键不稳定,或AnimatePresence本身与元素一起卸载。修复组件树,而不是动画。 - **旧版包用法混入:**教程和大模型补全经常会生成
framer-motion导入、staggerChildren或motion(Component)时代的写法。当前用法:motion包、delayChildren: stagger(),以及使用motion.create(Component)包装自定义组件(组件必须暴露 ref——React 18 中使用forwardRef,React 19 中使用普通的ref属性);如果 API 看起来过时,请查阅最新文档。 - **
popLayout需要定位方式:**退出的元素会采用绝对定位;使用static定位的父元素会导致它们位置错乱。 - **布局动画会扭曲圆角/阴影:**通过
style设置borderRadius/boxShadow(或对它们做动画),以便 Motion 能校正缩放;为会拉伸的子元素单独添加layout属性。 - **挂载时的弹簧动画显得迟缓又弹跳:**配置了物理参数(stiffness/damping)的弹簧会自行计算稳定所需时间,不遵循预期时长;请使用基于时长的弹簧(
type: "spring", duration, bounce),或使用 200 ms 补间动画实现进入效果。 - **Motion 值不会触发 React 重新渲染:**在 JSX 中渲染
{x.get()}会导致值过时——将 Motion 值作为motion元素的子元素渲染,或通过useMotionValueEvent订阅。 - **动画导致测试挂起或不稳定:**在测试设置中设置
MotionGlobalConfig.skipAnimations = true,并断言最终状态(参见集成参考)。 - **jsdom 没有布局:**布局动画和
whileInView需要真实的测量结果;单元测试状态逻辑,端到端测试视觉效果。
反模式
- “给所有东西都加动画——看起来更高级。” 数据密集型工具只需少量一致、简短且有明确用途的动画,就能显得精致;过多动态效果会减慢理解速度。
- “先跳过减少动态效果,之后再加。” 一开始就做好,只需在根级添加一行代码;之后再做,就需要全面审查。没有所谓的“之后”。
- “直接给高度加动画。” 布局属性会在主线程上造成卡顿;使用
layout属性或采用不同的设计。 - “AutoAnimate 不够好——用 Motion 重写列表。” 如果列表只需处理添加、删除和移动,一个 ref 就胜过三十行变体代码。
- “照搬那篇 2023 年博客文章里的 framer-motion 代码片段。” 请对照当前文档核实;软件包、导入方式和交错动画的写法都已更改。
输出
在 React 代码库中可用的动画代码:使用 motion/react 基本组件的组件(或按照 Step-1 表有意采用更简单的层级)、应用级的减少动态效果策略,以及能确保动画组件行为确定的测试设置。使用者是代码库的审查者和测试套件——交付的动画应已满足无障碍、性能和克制性要求。
相关内容
- UI/UX 设计指南技能(例如包含动画时长/含义规则的指南)是决定何时/为哪些内容添加动画的 METHOD 来源;本技能负责相应的实现。
- 组件样式技能(Radix/Tailwind)负责 Step-1 表中
data-stateCSS 层级。 - 路由器技能(例如 TanStack Router)负责路由结构;本技能只为路由视图添加过渡效果。
- 插画技能负责 Lottie/动画图像——与本技能相关,但不属于本技能的范围。
- 组件测试技能负责 RTL/vitest 测试框架,本技能的测试指南会接入其中。
渐进式披露
references/layout-and-shared-elements.md— 在为布局变化、共享元素(layoutId)或列表重排添加动画时加载。references/values-and-imperative.md— 使用动态值、变换/弹簧、命令式序列或数字滚动动画时加载。references/a11y-and-restraint.md— 配置减少动态效果、审查无障碍性或定义动态效果令牌/克制规则时加载。references/integration-and-testing.md— 将 Motion 与 Radix/shadcn、Tailwind 或路由器结合使用、使用 AutoAnimate,或测试涉及动画组件时加载。references/sources.md— 研究溯源;仅在审查说法时加载。
正文篇幅预算
description≤ 1,024 个字符(agentskills.io 上限)。- 正文 ≤ 约 500 行/5,000 个 token 为软性目标;超出的内容放在
references/中。
评论
支持 Markdown