9.1 动画
在大多UI框架中,动画实现大同小异,原理都相同的,就是在一段时间内快速多次改变外观,由于人眼存在视觉停留,所以最终看到的是一个连续的动画,这和看电影一样,我们将UI的一次更新成为一帧,对应屏幕刷新,而决定流畅度是重要因素就是FPS(Frame Per Second),就是每秒动画的帧数,显而易见,帧数约高动画越流畅!对于人眼来说帧率超过16,就比较流畅,超过30,就比较细腻,帧数大于30,人眼基本分辩不出来差别。由于每一帧都是UI改动的效果,那么60FPS,就是需要UI改动在16ms完成,持续的改变资源是比较耗费资源的,对设备性能要求比较高,所以UI系统中的动画的帧数的重要的性能指标,而在Flutter中,理想的情况下可以实现60FPS,这和原生基本一致。
Flutter中动画抽象
在Flutter中,官方对动画进行了抽象处理,主要包括Animation
、Curve
、Controller
、Tween
这四个角色,他们一起或者组合可以完成一个动画。
Animation
Animation
本身是一个抽象类,他和渲染没有直接关系,而他的作用是保存动画的查值和状态;其中一个比较常见的是Animation <double>
。Animation
会在一个时间段内一次生成一个取件的值,Animation
生成的值的曲线由Curve
类来实现,可以是曲线或者跳跃性的都行。根据Animation
对象的控制方式,动画可以正向运动(从开始到结束),也可以反向运行,甚至中间换方向或者其他操作。Animation
哈还可以生成其他的比如Color
、Offset
。在动画的每一帧,我们可以在Animation
取值来生成新的UI。
动画通知
我们可以通过AnimationController
来监听动画的每一帧变化,有如下方法
- 'addListen': 可以给
Animation
添加监听器,每一帧都会调用,可以借助setState()
来刷新UI。 addStatusListener()
: 可以给Animation
添加状态监听器;动画开始、结束、。
Curve
动画过程可以是匀速、加速、突变的,Flutter通过Curve
来实现曲线的变化。
可以通过CurveAnimation
来是指定实现Curve
的类,如:
_animation = CurvedAnimation(parent: _animationController,curve: Curves.bounceIn)
CurvedAnimation
可以通过包装AnimationController
和Curves.bounceIn
生成一个动画对象,他们通过这种方式关联起来。我们指定动画曲线为Curves.bounceIn
,他表示动画变化的效果,Curves
是一个静态函数,官方做了很多类型的动画,常用的有:
Curves 曲线 | 效果 |
---|---|
easInQuint | 慢速进,快速出 |
linear | 匀速 |
slowMiddle | 加速仅,中间平滑,后端加速 |
fastOutSlowIn | 慢速进,快速出 |
Curve
是个抽象类,我们也可以实现一个曲线,例如:
class SinLine extends Curve {
@override
double transform(double t) {
return sin(t * pi / 2);
}
}
animationController
animationController
用于控制动画,他包含动画的启动、停止、重复、反向等方法。animationController
会在动画的每一帧生成已新的值,默认情况下生成的值的范围是[0,1],例如:
final AnimationController _animationController = AnimationController(
vsync: this, duration: Duration(milliseconds: 1000));
生成的值的范围由属性lowerBound
和upperBound
来决定
final AnimationController controller = new AnimationController(
duration: const Duration(milliseconds: 2000),
lowerBound: 10.0,
upperBound: 20.0,
vsync: this
);
AnimationController
是继承Animatin<double>
,因此Animatin<double>
可以使用的地方,AnimatinController
都可以使用。
Ticker
当创建一个AnimationController
时,需要传递一个vsync
参数,他接受TickerProvider
类型对象,他主要职责是创建Ticker
,定义如下:
abstract class TickerProvider {
const TickerProvider();
Ticker createTicker(TickerCallback onTick);
}
Flutter在其启动时会绑定一个SchedulerBingding
,通过SchedulerBingding
可以给屏幕每次刷新添加回调,而Ticker
通过SchedulerBingding
来添加屏幕回调,这样的话,每次屏幕刷新都会回调用TickerCallback
.使用Ticker
(不是Timer)来驱动动画会防止屏外动画(动画超出屏幕)消耗不必要的资源,因为Flutter
中屏幕刷新时会通知绑定的SchedulerBinding
,而Ticker
是受SchedulerBinding
驱动的,所以锁屏后屏幕会停止刷新,所以Ticker
不会触发。
通常将SingleTickerProviderStateMixin
添加到State
定义中,然后将state
对象作为vsync
的值,这在后面的例子可以见到。
Tween
默认情况下,AnimationController
对象范围是[0,1],如果需要设置不同的范围或不同的类型,则可以使用Tween
来生成不同的类型的值,如:
final Tween t = ColorTween(begin: Colors.green,end: Colors.red);
Tween
不存储状态,只负责生成中间值,而他提供了evaluate(Animation<double> animation)
方法,获取动画当前的值。Animation
对象的当前值可以通过value()
方法取到。evaluate
函数还执行一些其它处理,例如分别确保在动画值为0.0
和1.0
时返回开始和结束状态。
Tween
是构造函数,需要begin
和end
两个参数,Tween
的职责就是定义输入范围到输出范围的映射。通常范围为[0,1],也可以自定义范围。
Tween
继承自Animatable<T>
,而不是Animation<T>
,Animatable
主要是定义动画的值的规则。
我们看下SizeTween
将动画范围映射两个面积之间的过度的例子。
final Tween size = SizeTween(begin: Size.zero,end: Size(100,100));
Tween.animate
使用Tween
对象,需要调用animate()
方法,然后传入一个控制器对象,例如:以下代码在1000毫米内生成从1到1000的整数值。
final AnimationController _animationController = AnimationController(
vsync: this, duration: Duration(milliseconds: 1000));
final Animation<int> _animation =
IntTween(begin: 0, end: 1000).animate(_animationController);
IntTween返回的是
Animation
,而不是Animatable
.
下边实例一个利用曲线生成一个颜色渐变的实例:
/// 在1000毫秒内生成值
final AnimationController _animationController = AnimationController(
vsync: this, duration: Duration(milliseconds: 1000));
/// 生成值是由红色渐变为绿色
final Animation _animation =
ColorTween(begin: Colors.red, end: Colors.green)
.animate(_animationController);
/// 生成的曲线规则是正向是 Curves.bounceInOut,逆向是Curves.linear
final Animation curv = CurvedAnimation(
parent: _animation,
curve: Curves.bounceInOut,
reverseCurve: Curves.linear);