SuperAPI AI 工具集

animate-expo

编程与开发

查看项目

更新于 10-15 14:20

技能介绍

用途

animate-expo 是一个面向 React Native 与 Expo 的动画构建技能。它把「想要一个动效」的请求,转化为在真机上经得起严格检验的实现——不是模拟器里、也不是开发模式下旗舰机上的效果。其知识来源是 Emil Kowalski 的动画哲学。

移动端动画的三个前提

技能中的所有决策都从移动端与 Web 的三点差异推导而来:

  1. 没有 hover。 Web 上依赖 hover 的每一种可供性,在移动端都必须落在按压、位置,或者干脆不做。
  2. 存在两个运行时。 Worklets(Reanimated 4)把这一点显式化:React Native 运行时负责 React 渲染与应用逻辑;UI 运行时负责 worklet 逐帧执行(另有可选的 worker 运行时处理后台工作)。任何触碰 RN 运行时的动画,只要应用在做别的事就会卡顿。全部功夫在于把动效留在 UI 运行时。
  3. 用户的手指就在元素上。 手势是主要输入,因此可中断性与速度传递不是打磨项,而是基线。

构建顺序

技能要求严格按顺序执行,其中第 1、2 步是总闸门:

  1. 这个元素到底该不该动画? 按使用频率分级:每天 100 次以上(标签切换、键盘开合、滚动、设置开关)不做动画,用平台默认或什么都不做;每天数十次(按压反馈、列表导航、行选择)只做几乎察觉不到的动效(150ms 以内)或不做;偶发(面板、模态、Toast、引导步骤)用标准动画;罕见/首次(成功状态、空状态插画、庆祝)才是「惊喜预算」所在。标签切换永远不要滑动——标签是平级的,滑动暗示了并不存在的层级。
  2. 目的是什么? 用一个词命名:反馈、空间一致性、状态指示、避免突兀变化、解释,或惊喜(仅限罕见层级)。命名不出来就不要做。
  3. 选最省的工具。 无手势的状态驱动变化用 Reanimated CSS 过渡;循环、多阶段或挂载即播放用 CSS 动画;挂载/卸载或列表重排用布局动画;手指触碰或由滚动派生的用 useSharedValue + Gesture + useAnimatedStyle;屏幕间转场用 Expo Router 的原生 stack;作为独立屏幕的底部面板用 presentation: 'formSheet';标签栏用 NativeTabs;上下文菜单用 Link.Menu / Link.Preview;折叠大标题用 headerLargeTitleEnabled;下拉刷新用 RefreshControl;跟随键盘的 UI 用 react-native-keyboard-controller;插画与庆祝用 Lottie;超大动画场景用 @shopify/react-native-skia。只有当值是连续或可中断的,才使用共享值。
  4. 选属性。 transform 与 opacity 是免费的,其余都会触发布局。唯一例外是无子元素的绝对定位元素(标签胶囊、进度条填充)。永远不要 scale(0),从 scale(0.9–0.97) + opacity: 0 开始。transform 是数组且顺序重要,translate 应放在前面。Android 阴影是 elevation,不要动画 elevation,改为对预阴影层做透明度交叉淡入。不要动画 BlurView 的 intensity。
  5. 时序还是弹簧。 涉及手指就用弹簧(弹簧能传递速度穿过中断,时序曲线会重启),其余用时序。Reanimated 的弹簧直接接受 Apple 的两个设计参数(duration 与 dampingRatio),而非 mass/stiffness/damping。缓动方面:进入/退出用 ease-out,屏幕内移动/变形用 ease-in-out,匀速运动用 linear,默认 ease-out,UI 上永远不要 ease-in。移动端 UI 动画保持在 300ms 以内。
  6. 让它离开 JS 线程。 永远不要在手势或滚动处理器里 setState;不要在 onUpdate 或滚动处理器里调度回 RN 运行时(scheduleOnRN 应放在 onEnd 或 useAnimatedReaction 中);不要在渲染期间读写共享值;用 .get() / .set() 而非 .value;从 worklet 调用的函数首行需要 'worklet'。
  7. 按压,而非 hover。 按下即反馈、松开才提交;可按压元素用 scale: 0.97、100–150ms;最小触摸目标 44×44pt(Android 48dp);用 pressRetentionOffset 避免手指轻微漂移取消按压;Android ripple 只在 Material 风格应用中使用。
  8. 触觉反馈。 值越过刻度用 Haptics.selectionAsync();吸附归位、面板卡位、拖拽提交用 Light 冲击;重物落地、破坏性操作用 Medium;操作成功或失败用 notificationAsync。三条绝对规则:与视觉同帧、每个用户操作只一次、永远不作为唯一反馈。从 worklet 触发触觉必须调度回 RN 运行时。
  9. 减少动态效果与无障碍。 用 useReducedMotion 或 ReduceMotion.System;减少动态意味着更少更柔和而非归零,保留解释状态变化的透明度与颜色变化,去掉位移、缩放、视差与过冲;屏幕转场改为 animation: 'fade'。文字会缩放,不要动画到硬编码高度,用 onLayout 测量或改为动画 transform。

会静默破坏动画的配置

  • 通过 Expo 安装以匹配 SDK 版本:npx expo install react-native-reanimated react-native-worklets。Expo 项目中 babel-preset-expo 会自动配置 worklets Babel 插件;只有不带该预设的裸 RN 项目才需手动添加,且必须放在列表最后。
  • GestureHandlerRootView 必须包裹应用,否则手势静默失效。
  • Reanimated 4 需要新架构。
  • Expo Go 不是性能环境,应在 release 构建中判断手感。
  • 在 ProMotion iPhone 上,第三方动画默认被限制在 60fps,除非设置 CADisableMinimumFrameDurationOnPhone。

适用场景与限制

适用于在 React Native 与 Expo 中构建需要在真机上表现良好的动画,包括按压反馈、拖拽关闭面板、滑动删除、折叠头部、列表入场、键盘同步 UI、标签指示器、屏幕转场等(技能提供 RECIPES.md 配方)。限制方面:技能明确列出「永远不要发布」的做法(如 PanResponder、手势中 setState、runOnJS、每帧 scheduleOnRN、渲染期读写共享值、动画 height/width/margin/flex/top、动画 BlurView intensity 或 Android elevation、在虚拟列表行上用 entering、用 JS 重建屏幕转场、标签间滑动、UI 元素上用 Easing.in、scale(0) 入场、仅按距离的关闭阈值、边界硬停、每帧触觉或仅触觉反馈、在 Expo Go 或模拟器中判断手感)。部分能力有平台限制,如 Link.Menu / Link.Preview 与 headerLargeTitleEnabled 仅限 iOS。

输出与语气

技能要求先写出代码,然后用最多几行说明:闸门结果(频率层级与命名目的,说明拒绝了什么及原因)、所用材料(工具、属性、弹簧或曲线加时长、线程)、以及需要在真机上感受检查的内容(手势、速度传递与触觉时机无法从代码判断)。代码就是交付物,不要扩写成报告。语气上要有主见且简短。