9.1 动画

在大多UI框架中,动画实现大同小异,原理都相同的,就是在一段时间内快速多次改变外观,由于人眼存在视觉停留,所以最终看到的是一个连续的动画,这和看电影一样,我们将UI的一次更新成为一帧,对应屏幕刷新,而决定流畅度是重要因素就是FPS(Frame Per Second),就是每秒动画的帧数,显而易见,帧数约高动画越流畅!对于人眼来说帧率超过16,就比较流畅,超过30,就比较细腻,帧数大于30,人眼基本分辩不出来差别。由于每一帧都是UI改动的效果,那么60FPS,就是需要UI改动在16ms完成,持续的改变资源是比较耗费资源的,对设备性能要求比较高,所以UI系统中的动画的帧数的重要的性能指标,而在Flutter中,理想的情况下可以实现60FPS,这和原生基本一致。

Flutter中动画抽象

在Flutter中,官方对动画进行了抽象处理,主要包括AnimationCurveControllerTween这四个角色,他们一起或者组合可以完成一个动画。

Animation

Animation本身是一个抽象类,他和渲染没有直接关系,而他的作用是保存动画的查值和状态;其中一个比较常见的是Animation <double>Animation会在一个时间段内一次生成一个取件的值,Animation生成的值的曲线由Curve类来实现,可以是曲线或者跳跃性的都行。根据Animation对象的控制方式,动画可以正向运动(从开始到结束),也可以反向运行,甚至中间换方向或者其他操作。Animation哈还可以生成其他的比如ColorOffset。在动画的每一帧,我们可以在Animation取值来生成新的UI。

动画通知

我们可以通过AnimationController来监听动画的每一帧变化,有如下方法

  • 'addListen': 可以给Animation添加监听器,每一帧都会调用,可以借助setState()来刷新UI。
  • addStatusListener(): 可以给Animation添加状态监听器;动画开始、结束、。

Curve

动画过程可以是匀速、加速、突变的,Flutter通过Curve来实现曲线的变化。

可以通过CurveAnimation来是指定实现Curve的类,如:

_animation = CurvedAnimation(parent: _animationController,curve: Curves.bounceIn)

CurvedAnimation可以通过包装AnimationControllerCurves.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));

生成的值的范围由属性lowerBoundupperBound来决定

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.01.0时返回开始和结束状态。

Tween是构造函数,需要beginend两个参数,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);

results matching ""

    No results matching ""