这是一个面向 Expo/React Native 项目的动画与动效规范技能,基于 Reanimated 4 在 UI 线程上实现动画。它的核心思路是:把应用里每一个会动的元素都作为一个「动效时刻」(moment)登记到目录文件 src/ui/motion/motion-moments.ts 中,只登记一次,写明其类型、审阅者能在应用中定位到的触发点、以 token 名称表示的时序(duration、easing、delay、stagger,或 spring,或 navigator: 'stack'),以及减弱动态效果时的替代表现。目录是纯数据,只使用类型导入。
主要能力与硬性规则包括:每个含动画的文件都必须声明它属于哪个时刻,通过 timingOf、springOf、delayOf、staggerOf(MOTION_MOMENTS.<id>) 取得时序;无法导入目录的文件(如共享按压面、渲染原生 Stack 的文件)需由时刻在 files 中列出,并由该文件消耗时刻指定的 token,且不能有已声明却未使用的时刻。每个时刻都要声明减弱动效下的表现,如反馈类为立即显示、淡入淡出或无,状态类为立即或淡入淡出,转场类为淡入淡出、立即或无,注意力类为静止或无,庆祝类为静止、淡入淡出或无等,且关闭动效后最终状态必须一致。减弱动效由根布局统一挂载 MotionConfig 与 watchSystemA11y() 向下传递;根无法覆盖的部分(如 Reanimated CSS 动画、原生 Stack 转场)需自行读取 isReducedMotion 或 useScreenAnimation(),且不使用 React Native 的 LayoutAnimation。循环必须通过 useMotionLoop(moment, { isFocused }) 运行,仅在屏幕聚焦且应用活跃且未减弱动效时运行。动效只装饰状态、不承载状态,操作应先提交再动画,动画期间不得禁用输入;所有动画须可中断,从当前值重新指向而非先复位,拖拽结束的弹簧要带上手势速度;序列使用 withDelay、withSequence 而非 setTimeout;帧内不得调用 scheduleOnRN 或 React setter;优先动画 transform 与 opacity,布局属性需加 // motion-layout 标记注释;庆祝动画受 CELEBRATION_LIMITS 约束(库上限 2000 毫秒、40 个粒子),只播放一次且可跳过。
使用方式:先确认 package.json 依赖 react-native-reanimated(可用 npx expo install react-native-reanimated react-native-worklets 安装),然后把模板复制到 src/ui/motion/,按需补齐 a11y 与 hooks 模板;将目录中的 ROOT_STACK_FILE 替换为渲染根 Stack 的文件;在根布局启动 watchSystemA11y 并在导航器外挂载 MotionConfig。随后决定某处是否应动、声明时刻、按场景选用合适的工具(状态变化、进入退出、拖拽、循环、庆祝、CSS 过渡、必须动画宽度的填充、功能性定时器等)编写实现,最后编写测试并依次运行 npm run typecheck、npx eslint . --max-warnings 0、npx jest --ci 以及 node ${CLAUDE_SKILL_DIR}/scripts/check-motion.mjs . 直到输出 RESULT: PASS。还需在指定 iOS 模拟器与 Android 模拟器的 release 构建上,分别在开启动效与减弱动效下实测记录每个改动的时刻,并有意中断动画、在真机上体验触觉反馈。
适用场景:需要在 Expo 应用中统一管理转场、底部弹层、拖拽松手、循环提示、庆祝效果等动效,并确保无障碍「减弱动态效果」设置被全面遵守的团队。限制:该技能面向 Reanimated 4 与 Expo 生态;若不依赖 react-native-reanimated,校验会输出 NOT APPLICABLE;部分检查在相关文件尚未创建时会输出 SKIP;动画手感需在双平台 release 构建上人工验证,模拟器或开发模式下的判断不足为凭。