Animate with springs
System & Services 进阶 20m

用弹簧动画让界面活起来

Animate with springs

2023年6月5日

在 Apple 官方观看视频

一句话判断

这不仅仅是一节动画 API 教程,而是一堂关于”为什么弹簧是最好动画”的深度思考。

这场 Session 讲了什么

Session 从物理原理出发,深入讲解了为什么弹簧动画是 UI 动画的最佳选择。核心论点有两个:弹簧是唯一能同时保证位置和速度连续性的动画类型(特别是在有初始速度的情况下),以及弹簧动画的运动曲线更接近真实世界的物理运动。

与 Ease In/Out 和 Linear 动画的对比很有说服力。Ease In/Out 在静态起始情况下表现不错,但在配合手势使用时,由于无法表示初始速度,会产生突兀的速度跳变。Linear 动画在起始和结束点都有速度跳跃,不适合大多数 UI 动画场景。

弹簧的物理模型由三个参数定义:质量(mass)、刚度(stiffness)和阻尼(damping)。SwiftUI 提供了 spring 动画修饰符,你不需要直接操作这些物理参数——可以通过 duration 和 bounce 来控制动画行为。

iOS 上的 app 启动动画是弹簧动画多属性协作的经典案例。不同属性使用不同的弹簧参数、不同的起始和结束时间,组合出自然流畅的动画效果。

值得深挖的点

多属性动画的不同步结束时间是弹簧动画的一个特点,而不是缺陷。真实世界中,运动中的物体因摩擦力减速而停止的时间通常不会完全对齐——弹簧动画正是模拟了这种自然行为。

SwiftUI 现在会自动追踪手势中的速度变化。当手势结束时,弹簧动画可以自动继承手势的速度作为初始速度,创造出”物理上连贯”的动画效果。你不需要写额外的代码就能获得这个行为。

代码片段

// 基本弹簧动画
ContentView()
    .animation(.spring(duration: 0.5), value: isActive)

// 带弹跳的弹簧
ContentView()
    .animation(.spring(duration: 0.5, bounce: 0.3), value: isExpanded)

// 无弹跳的弹簧(最常见的 iOS 动画)
ContentView()
    .animation(.spring(duration: 0.3, bounce: 0), value: position)

// 弹簧参数说明
// duration: 动画大致持续时间
// bounce: 弹跳程度 (0 = 无弹跳, 1 = 最大弹跳)
// 默认 bounce 为 0,提供平滑自然的减速效果

// 手势配合弹簧动画
// SwiftUI 自动追踪手势速度
// 手势结束时弹簧动画自动继承初始速度

最佳实践

  • 优先使用弹簧动画而非 Ease In/Out 或 Linear
  • 弹簧不等于弹跳——无弹跳的弹簧仍然是最佳选择
  • 让不同属性使用不同弹簧参数,创造更自然的动画
  • 信任 SwiftUI 的手势速度追踪,不需要手动传递速度
  • iOS 系统动画广泛使用无弹跳弹簧

还有什么值得关注

  • 弹簧动画的参数(duration、bounce)比物理参数(mass、stiffness、damping)更直观
  • 多属性动画的不同步结束是特性而非 bug
  • Linear 动画适合旋转指示器等特殊场景,其他场景慎用
WWDC 2023