什么是动画?
-
人类具有“视觉暂留”的特性,人的眼睛看到画面在0.34秒内不会消失。利用这一原理,在一幅画还没有消失前播放下一幅画,就会给人造成一种流畅的视觉变化效果。因此,当一段时间内,连续的对UI进行改变;人的眼睛就会产生视觉暂留,最终看到的就是一个“连续”的动画;对于人眼来说,一般帧率只要超过16FPS,就会比较流畅,超过32FPS就会较为细腻平滑,超过32FPS基本感受不到差别了。
Flutter 动画简述
- 由于 Flutter 动画中, 每一帧的变化都是需要对UI进行改变,短时间内连续的改变UI输出会非常的消耗资源的,因此在UI系统中,动画的平均帧率是重要的性能指标;Flutter 动画在理想状态下是可以实现60FPS的,这和原生应用能达到的帧率是基本是持平;Flutter 中对动画进行了抽象,主要通过
Animation、Curve、Controller、Tween
配合来完成动画-
Animation
对象是Flutter动画库中的一个核心类,它生成指导动画的值。 -
Animation
对象知道动画的当前状态(例如,它是开始、停止还是向前或向后移动),但它不知道屏幕上显示的内容。 -
AnimationController
管理Animation
。 -
CurvedAnimation
将过程抽象为一个非线性曲线. -
Tween
在正在执行动画的对象所使用的数据范围之间生成值。例如,Tween
可能会生成从红到蓝之间的色值,或者从 0 到 255。 - 使用
Listeners
和StatusListeners
监听动画状态改变。
-
Animation
Flutter中的动画系统基于 Animation
对象的,widget 可以在 build
函数中读取 Animation
对象的当前值, 并且可以监听动画的状态改变。
Animation
对象本身和 UI 渲染没有任何关系。Animation
是一个抽象类,它拥有其当前值和状态(完成或停止)。其中一个比较常用的Animation
类是Animation<double>
。Animation
对象是一个在一段时间内依次生成一个区间之间值的类, 对象的输出可以是线性的、曲线的、一个步进函数或者任何其他可以设计的映射, 根据Animation
对象的控制方式,动画可以反向运行,甚至可以在中间切换方向。Animation
还可以生成除double
之外的其他类型值,如:Animation<Color>
或Animation<Size>
;Animation
对象有状态,在动画的每一帧中,我们可以通过Animation
对象的value
属性获取动画的当前状态值。-
动画监听
- 使用
addListener()
给Animation
添加帧监听器,在每一帧都会被调用。最常见的操作是改变状态后调用setState()
来触发UI重建。 - 使用
addStatusListener()
给Animation
添加 “动画状态改变” 监听器;动画开始、结束、正向或反向(见AnimationStatus
定义)时会调用状态改变的监听器。
- 使用
CurvedAnimation
-
CurvedAnimation
将动画过程定义为一个非线性曲线 - Flutter 中通过
Curve
(曲线)来描述动画过程,我们把匀速动画称为线性的(Curves.linear
),而非匀速动画称为非线性的
final CurvedAnimation curve =
new CurvedAnimation(parent: controller, curve: Curves.easeIn);
-
Curves
类定义了许多常用的曲线,我们也可以选择自定义
class ShakeCurve extends Curve {
@override
double transform(double t) {
return math.sin(t * math.PI * 2);
}
}
-
CurvedAnimation
和AnimationController
都是Animation<double>
类型,CurvedAnimation
可以通过包装AnimationController
和Curve
生成一个新的动画对象 ,通过这种方式来将动画和动画执行的曲线进行关联;
AnimationController
-
AnimationController
是一个特殊的Animation
对象,在屏幕刷新的每一帧,就会生成一个新的值。它包含动画的启动forward()、stop()、reverse()
等方法;默认情况下,AnimationController
在给定的时间段内会线性的生成0.0~1.0
的数字,另外在创建AnimationController
时,需要传递一个vsync
参数,存在vsync
时会防止动画的UI不在当前屏幕时消耗不必要的资源 。 例如,下面代码创建一个Animation对象,但不会启动它运行
final AnimationController controller = new AnimationController(
duration: const Duration(milliseconds: 2000), vsync: this);
注:vsync 对象会绑定动画的定时器到一个可视的 widget,当 widget 不显示时,动画定时器将会暂停,当 widget 再次显示时,动画定时器重新恢复执行,这样就可以避免动画相关UI不在当前屏幕时消耗资源。 如果要使用自定义的State 对象作为 vsync 时,请包含 TickerProviderStateMixin;
注意: 在某些情况下,值(position,指动画的当前值)可能会超出AnimationController 的 0.0-1.0 的范围。例如,fling() 函数允许您提供速度(velocity)、力量(force)、position(通过Force对象)。位置(position)可以是任何东西,因此可以在0.0到1.0范围之外。 CurvedAnimation 生成的值也可以超出0.0到1.0的范围。根据选择的曲线,CurvedAnimation 的输出可以具有比输入更大的范围。例如,Curves.elasticIn 等弹性曲线会生成大于或小于默认范围的值。
-
AnimationController
生成数字的区间可以通过lowerBound
和upperBound
来指定
final AnimationController controller = new AnimationController(
duration: const Duration(milliseconds: 2000),
lowerBound: 10.0,
upperBound: 20.0,
vsync: this
);
-
AnimationController
派生自Animation<double>
,因此可以在需要Animation
对象的任何地方使用。 但是,AnimationController
具有控制动画的其他方法。例如,.forward()
方法可以启动动画。数字的产生与屏幕刷新有关,因此每秒钟通常会产生60个数字,在生成每个数字后,每个Animation
对象调用添加的Listener
对象。 - 当动画开始执行后会开始生成动画帧,屏幕每刷新一次就是一个动画帧,每一个动画帧,会随着动画的曲线来生成当前的
Animation.value
,然后根据当前的Animation.value
去构建UI,当所有动画帧依次触发时,Animation.value
会依次改变,构建的UI也会跟随着依次变化,最终我们可以看到一个完成的动画。 另外在动画的每一帧,Animation
对象会调用其帧监听器,等动画状态发生改变时会调用状态改变监听器。
Tween
- 默认情况下,
AnimationController
对象的范围为0.0~1.0
。如果您需要不同的范围或不同的数据类型,则可以使用Tween
来配置动画以生成不同的范围或数据类型的值;Tween
是一个无状态(stateless
)对象,需要begin
和end
值。Tween
的唯一职责就是定义从输入范围到输出范围的映射。输入范围通常为0.0~1.0
,但这不是必须的;
final Tween doubleTween = new Tween<double>(begin: -200.0, end: 0.0);
-
Tween
继承自Animatable<T>
,而非Animation<T>
。Animatable
与Animation
相似,不是必须输出double
值。例如,ColorTween
指定两种颜色之间的过渡
final Tween colorTween =
new ColorTween(begin: Colors.transparent, end: Colors.black54);
-
Tween
对象不存储任何状态,它提供了evaluate(Animation<double> animation)
方法将映射函数应用于动画当前值。Animation
对象的当前值可以通过value()
方法取到。evaluate
函数还执行一些其它处理,例如分别确保在动画值为0.0~1.0
时返回开始和结束状态 - 使用
Tween
对象,需要调用其animate()
方法,传入一个控制器对象。
// 需要注意的是 `animate()` 返回的是一个 `Animation`,而不是一个 `Animatable`
final AnimationController controller = new AnimationController(
duration: const Duration(milliseconds: 500), vsync: this);
Animation<int> alpha = new IntTween(begin: 0, end: 255).animate(controller);
- 以下示例构建了一个控制器、一条曲线和一个
Tween
final AnimationController controller = new AnimationController(
duration: const Duration(milliseconds: 500), vsync: this);
final Animation curve =
new CurvedAnimation(parent: controller, curve: Curves.easeOut);
Animation<int> alpha = new IntTween(begin: 0, end: 255).animate(curve);