Flutter-动画-原理篇

时间:2019-09-18
本文章向大家介绍Flutter-动画-原理篇,主要包括Flutter-动画-原理篇使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

一、动画关键类的源码分析

1、Animation

Animation没有做什么与动画有关的事情,它只是记录了动画的“状态”、当前的“值”和一些注册回调接口的方法。

abstract class Animation<T> extends Listenable implements ValueListenable<T> {

  const Animation();

  // "值"变化的回调
  @override
  void addListener(VoidCallback listener);

  @override
  void removeListener(VoidCallback listener);
  // 状态回调
  void addStatusListener(AnimationStatusListener listener);

  void removeStatusListener(AnimationStatusListener listener);
  // 动画状态
  AnimationStatus get status;
  // 动画当前“值”
  @override
  T get value;

  bool get isDismissed => status == AnimationStatus.dismissed;
  
  bool get isCompleted => status == AnimationStatus.completed;

  //...
  
}

2、Tween

Tween记录了一个区间的begin和end。举个例子来说:begin=100   end=200 

    • t=0.1  lerp() = 110
    • t=0.5  lerp() = 150
    • t=1.0  lerp() = 200
class Tween<T extends dynamic> extends Animatable<T> {
  //...
  // 起始值
  T begin;
  // 结束值
  T end;

  // 计算在特定区间某个时刻的返回值
  // t是[0.0,1.0]在某个时刻的比例系数
  @protected
  T lerp(double t) {
    assert(begin != null);
    assert(end != null);
    return begin + (end - begin) * t;
  }
  // 外部调用值的变换
  @override
  T transform(double t) {
    if (t == 0.0)
      return begin;
    if (t == 1.0)
      return end;
    return lerp(t);
  }
  //...
}

我们在使用Tween的时候,必须调用Tween.animate()方法。其animate()方法是Tween继承自Animatable<T>类而来的。

// 创建一个Animation
anim = Tween<Offset>(begin: Offset(0, 0),end: Offset(0, 2),).animate(_controller);
// 位移Tween类中,继承自Animatable<T>类
Animation<T> animate(Animation<double> parent) {
    return _AnimatedEvaluation<T>(parent, this);
}
 

class _AnimatedEvaluation<T> extends Animation<T> with AnimationWithParentMixin<double> {
  //...
  @override
  final Animation<double> parent;
  // 这个变量就是Tween
  final Animatable<T> _evaluatable;
  
  @override
  T get value => _evaluatable.evaluate(parent);
  //...
}

由此可以得出animation.value的值来自Tween.evaluate().

3、AnimationController

AnimationController的实现相比较其它的动画核心类来说会比较复杂。那本文章就从两个方面简单分析一下AnimationController的实现。

    • AnimationController的声明
      • AnimationController实现了两个比较重要的Mixin,一个动画值变化的Listener(AnimationLocalListenersMixin),另一个是动画状态改变的Listener(AnimationLocalStatusListenersMixin)。这里就不帖两个Listener的源代码了,感兴趣的可以去Flutter SDK看一下。
// AnimationController的声明
class AnimationController extends Animation<double>
  with AnimationEagerListenerMixin, AnimationLocalListenersMixin, AnimationLocalStatusListenersMixin {
    • AnimationController的构造函数

AnimationController可以接收很多参数,其中最重要的就是@required TickerProvider vsync,因为它才是动画动起来的根本原因(下面会讲)。

AnimationController({
    double value,
    this.duration,// 动画执行时间
    this.reverseDuration,// 动画反向执行时的时间长度(默认和duration相同)
    this.debugLabel,//debug模式下使用的标签
    this.lowerBound = 0.0,// 动画执行完一遍回到的值
    this.upperBound = 1.0,// 动画完成的值
    this.animationBehavior = AnimationBehavior.normal,// 动画行为(一遍还是重复执行)
    @required TickerProvider vsync,
  }) : assert(lowerBound != null),
       assert(upperBound != null),
       assert(upperBound >= lowerBound),
       assert(vsync != null),
       _direction = _AnimationDirection.forward {
    _ticker = vsync.createTicker(_tick);// 计时器
    _internalSetValue(value ?? lowerBound);
  }

4、CurvedAnimation

CurvedAnimation是一个非线性曲线的Animation,它继承Animation,它与Animation的区别是在取值的时候按照特定非线性曲线函数生成的值。

// 该函数的具体实现方式
  @override
  double get value {
    final Curve activeCurve = _useForwardCurve ? curve : reverseCurve;

    final double t = parent.value;
    if (activeCurve == null)
      return t;
    if (t == 0.0 || t == 1.0) {
      assert(() {
        final double transformedValue = activeCurve.transform(t);
        final double roundedTransformedValue = transformedValue.round().toDouble();
        if (roundedTransformedValue != t) {
          throw FlutterError(
            'Invalid curve endpoint at $t.\n'
            'Curves must map 0.0 to near zero and 1.0 to near one but '
            '${activeCurve.runtimeType} mapped $t to $transformedValue, which '
            'is near $roundedTransformedValue.'
          );
        }
        return true;
      }());
      return t;
    }
    return activeCurve.transform(t);
  }

5、SingleTickerProviderStateMixin

我们创建动画的时候,必须要传递一个TickerProvider参数,SingleTickerProviderStateMixin继承TickerProvider。它本身有两个作用:

    • 创建动画计时器。
    • 关联Widget生命周期(实际上给了Ticker)。
@optionalTypeArgs
mixin SingleTickerProviderStateMixin<T extends StatefulWidget> on State<T> implements TickerProvider {
  Ticker _ticker;
  // 创建Ticker
  @override
  Ticker createTicker(TickerCallback onTick) {
    assert(() {
      if (_ticker == null)
        return true;
      throw FlutterError(
        'xxxxxxx'
      );
    }());
    _ticker = Ticker(onTick, debugLabel: kDebugMode ? 'created by $this' : null);
    return _ticker;
  }
  //...
  @override
  void didChangeDependencies() {
    // 关联widget的生命周期,实际上传给了Ticker
    if (_ticker != null)
      _ticker.muted = !TickerMode.of(context);
    super.didChangeDependencies();
  }
  //...
}

二、动画动起来的过程

这时候我们在外面通过anim.addListener(() {setState(() {});});不断的视图层进行重绘,则控件便动了起来。

三、总结

  • 抽象类Animation仅仅保存了动画状态和回调函数。
  • AnimationController和CurvedAnimation都继承Animation。
  • AnimationController是动画的控制器,控制动画的开始和结束,接收动画执行的时间。
  • Tween用于限定动画的值的区间。
  • SingleTickerProviderStateMixin用于创建Ticker和绑定Widget生命周期。
  • Ticker是一个时间定时器,每一个动画帧每一个动画帧都会调用回调函数。

参考文献:1、感谢Flutter中文网提供的资料。2、感谢简书博主

原文地址:https://www.cnblogs.com/819158327fan/p/11540095.html