时间曲线、弹簧与运动轨迹
react-native-shared-hero 中的每一次飞行都由两个正交维度共同决定:
- 空间运动轨迹(
motionPath):元素中心点在屏幕上的移动路径("linear"直线或"arc"弧线)。 - 时间推进器(
spring或duration+easing):动画进度如何随时间在轨迹上推进。
交互式演练场
使用下方的交互式模拟器对比弹簧物理与固定时长曲线的差异,切换 "arc" 与 "linear" 轨迹,并一键复制生成的 <SharedHero> JSX 代码:
原生窗口覆盖层飞行动画的交互式模拟。点击飞行按钮或拖动进度滑块,观察尺寸、圆角、淡入淡出曲线与运动轨迹的变化。
// Source screen
<SharedHero
id="poster-42"
mode="morph"
style={{ borderRadius: 10 }}
>
<Image source={{ uri }} style={styles.thumb} />
</SharedHero>
// Destination screen
<SharedHero
id="poster-42"
mode="morph"
motionPath="arc"
spring={{ damping: 22, stiffness: 260, mass: 1 }}
style={{ borderRadius: 24 }}
>
<Image source={{ uri }} style={styles.heroHeader} />
</SharedHero>原生弹簧物理(spring)
当向 <SharedHero> 传入 spring 对象时,它会覆盖 duration 和 easing,直接调用系统原生的弹簧物理求解器驱动动画:
- iOS:由 Core Animation 渲染服务直接执行的
CASpringAnimation。 - Android:基于
Choreographer帧回调运行的androidx.dynamicanimationSpringAnimation。
| Android (Spring 对比 Duration) | iOS (Spring 对比 Duration) |
|---|---|
<SharedHero
id={`card-${item.id}`}
mode="morph"
spring={{
damping: 22,
stiffness: 260,
mass: 1,
}}
>
<CardDetail item={item} />
</SharedHero>弹簧参数与默认值
| 参数 | 默认值 | 说明 |
|---|---|---|
damping | 22 | 粘性阻尼系数 (c)。较小的值(12 至 16)会产生明显的回弹;22 至 28 之间则能快速利落地收敛到位。 |
stiffness | 260 | 弹簧刚度常数 (k)。数值越大(320 至 450),起步阶段的加速响应越干脆。 |
mass | 1 | 飞行物体的虚拟质量 (m)。iOS 原生支持 CASpringAnimation.mass;Android 的 SpringForce 固定为单位质量(1),并将 damping 与 stiffness 转换为等效阻尼比 (\zeta = \frac{c}{2\sqrt{k}}) 与固有频率。 |
为什么弹簧动画在触屏设备上更自然?
基于 duration 的缓动曲线无论移动距离长短都固定耗时相同的毫秒数;而弹簧物理在半空被打断接管时能够自然延续速度感(参见半空飞行打断与接管),并在接近目标槽位时平滑减速。
固定时长与三次贝塞尔缓动(duration + easing)
如果未传入 spring,飞行将按 duration 毫秒(默认 320 ms)结合 easing 指定的三次贝塞尔预设(默认 "standard")运行:
<SharedHero
id={`poster-${item.id}`}
duration={380}
easing="emphasized"
>
<PosterDetail item={item} />
</SharedHero>可用的 easing 预设
| 预设名称 | 控制点 ((x_1, y_1, x_2, y_2)) | 曲线特征 |
|---|---|---|
"standard"(默认) | (0.2, 0.0, 0.0, 1.0) | Material 3 标准曲线:起步迅速,收尾减速柔和悠长。 |
"emphasized" | (0.3, 0.0, 0.0, 1.0) | 比 "standard" 更具爆发力的初速度,非常适合全屏卡片展开。 |
"easeOut" | (0.0, 0.0, 0.58, 1.0) | 以最大速度起步,接近终点时平滑减速。 |
"easeInOut" | (0.42, 0.0, 0.58, 1.0) | 首尾对称地加速与减速。 |
"easeIn" | (0.42, 0.0, 1.0, 1.0) | 缓慢起步并持续加速直至终点。 |
"linear" | (0.0, 0.0, 1.0, 1.0) | 全程匀速移动。 |
运动轨迹:"linear" 与 "arc"
默认情况下(motionPath="linear"),飞行视图的中心点沿直线从源中心 ((x_0, y_0)) 移动到目标中心 ((x_1, y_1))。
当元素同时在水平和垂直方向上发生较大位移时(例如从网格右下角的小缩略图飞向详情页顶部的居中大图),笔直的对角线往往显得有些生硬。设置 motionPath="arc" 会将中心点轨迹切换为二次贝塞尔弧线:
[ B(t) = (1 - t)^2 P_0 + 2(1 - t)t P_{\text{ctrl}} + t^2 P_1 ]
其中控制点 (P_{\text{ctrl}}) 沿垂直于弦 (P_0 \to P_1) 的屏幕上方偏移弦长的 18%。
Android (motionPath="arc") | iOS (motionPath="arc") |
|---|---|
<SharedHero
id={`badge-${item.id}`}
mode="morph"
motionPath="arc"
duration={420}
>
<BadgeExpanded item={item} />
</SharedHero>哪一端的配置生效?
一次过渡中同时存在源视图与目标视图两个 <SharedHero>:
- 去程飞行(源视图 -> 目标视图):由目标视图上的
mode、duration、spring、easing、fadeMode与motionPath决定动画行为。 - 返程飞行(目标视图 -> 源视图):同样由正在卸载的目标视图上的动画属性驱动返程动画。这样你只需在详情页配置一次参数,去程和返程就能自动保持对称,无需在列表项中重复声明时间配置。