zoukankan      html  css  js  c++  java
  • 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.
    '
                '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();
      }
      //...
    }

    二、动画动起来的过程

    WechatIMG20.png

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

    三、总结

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

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

  • 相关阅读:
    【转载】褪去华衣 裸视学习 探讨系列
    最简单的视频网站(JavaEE+FFmpeg)
    过段时间要换博客了
    计网3
    计网1
    物理层计算题
    计网4
    子网划分与CIDR
    百度=残留在墙后的垃圾
    计网2
  • 原文地址:https://www.cnblogs.com/819158327fan/p/11540095.html
Copyright © 2011-2022 走看看