动画的英文有很多表述,如 Animation、Cartoon、Animated等。其中较正式的 「Animation」 一词源自于拉丁文字根 anima,意思为「灵魂」,动词 Animate 是「赋予生命」的意思,引申为使某物活起来的意思。所以动画可以定义为使用绘画的手法,创造生命运动的艺术。
在 SwiftUI 中,动画的实现非常简洁而强大,它提供了一系列内置的动画类型和方法,让我们通过少量代码就能创建出引人注目的动画效果。不过在深入讲解 「SwiftUI 动画」之前,我们先探索下怎么产生的动画?
产生动画的步骤
大多数动画的创作流程可概括为三个核心步骤:
- 启动:定义一个「初始状态」,即动画开始之前的布局和外观。
- 变化:指的是动画「动起来的过程」,这一阶段中属性(如位置、大小、颜色等)发生变化,形成动画效果。
- 终止:确定「最终状态」,动画完成后视图应达到的布局和外观。
动画启动的时机
动画启动需要依赖于某种触发因素。所谓「触发因素」,指的是那些能够激发或引发动画开始的事件或动作。这种触发可以是按下一个按钮、某个值的改变,或者是如屏幕显示这样的特定事件。
如上图所示,当「点击」按钮后,圆圈「由上到下」移动了位置。 此时,满足了动画产生的所有条件:有「启动-变化-终止」的变化过程,也有「触发因素」,现在看下在 SwiftUI 中的实现:
swift
HStack {
Button("按钮") {
changed.toggle()
}.buttonStyle(.borderedProminent)
.controlSize(.large)
Spacer()
Circle().fill(.blue)
.frame(width: 100)
.offset(y: changed ? 200 : 0)
}.padding()
- 按钮点击触发了
change
「值」的改变(false -> true)。 change
值的改变又使得小球的「偏移量」在y轴产生了改变(0 -> 200)
问题来了,为什么没有看到「动画效果」?
SwiftUI 实现动画效果
实现起来非常简单,只需为小球Circle()
增加一个.animate()
修饰符。
swift
Circle().fill(.blue)
.frame(width: 100)
.offset(y: changed ? 200 : 0)
.animation(.easeIn, value: changed)
如下图所示,此时小球移动过程中产生了动画的渐进效果:
看下.animate()
修饰符的文档:
从文档中的释义我们能得出几个结论:
to this view
:意味着修饰的是视图,所以无论是文本(Text)、图片(Image)或容器视图(HStack、VStack、List等),只要是视图,都可用动画修饰符,区别是修饰文本、图片影响到的是「视图本身」,而修饰容器视图还会影响到它的「子视图」。the specified value changed
:SwiftUI 之所以能识别哪里应该产生动画,就是通过监控value
值的变化来实现的,所以控制值、改变值是产生动画的必要条件。它相当于用来定义动画产生的「启动」和「终止」。Applies the given animation
:这句话描述的就是「变化」的过程,通过修改第一个参数_ animation: Animation
, 可以定制不同的变化效果。
动画曲线
「动画曲线」(变化效果)是表达整个动画持续时间内速度的一种方式。在前面的例子中,看到了easeIn
的用法。以图形方式表达如下: 开始启动的时候变化「较慢」,后面变化的速度会逐渐「加快」。所以对应上例中的小球移动来说,小球「启动」的移速会较慢,随着时间的流逝,「速度」会逐渐加快,直到「终止」。 下面是 4 种常用的 Animation 种类(还有比如spring
、interactiveSpring
、timingCurve
等用法后面会讲到,目前为止,了解这 4 种足够一般情况下的使用了)。
让我们通过一个具体的 SwiftUI 示例,来具体了解下这四种动画曲线的区别:
尽管都设置了「同样」( 2 秒)的动画执行「时间间隔」,但是不同的动画曲线使得它们执行的或「快」或「慢」,肉眼可见的看到差别。
上例中,均使用了改变视图的偏移量(Offset)产生移动视图的动画,除此之外,还有其它的属性能够产生动画吗?
后续的章节中,会陆续列举各种各样的案例来产生动画。所以到目前为止,我们只需要知道一点:
大多数「数值型」的属性都可以用来动画化。
例如:
视图的偏移量(改变位置)、视图的高与宽(改变尺寸)甚至包括视图的颜色等,用数值来"表达"(可计算)的属性大多数(不是全部)都能用来产生「动画」。
插值动画
SwiftUI 构建在响应式编程的概念之上,其中视图的显示和行为是由「数据状态」决定的。当这些数据状态发生「变化」时,SwiftUI 自动「重新计算」视图(重新渲染),确保用户界面与数据状态保持一致。
当一个「可动画化」的属性改变时,SwiftUI 不仅仅是简单地重新计算视图。而是使用了「插值」(interpolation)技术在旧值和新值之间计算出「中间值」。这个过程会在一段时间内持续进行,形成了一个平滑的动画效果。
如上例中的小球移动,它的偏移量从 0 逐渐增长到 200,SwiftUI 将计算出 0 到 200 之间的所有中间值,并逐渐应用这些值,从而产生移动的效果。