Flutter-動畫-原理篇

1、動畫關鍵類的源碼分析

一、Animation

Animation沒有作什麼與動畫有關的事情,它只是記錄了動畫的「狀態」、當前的「值」和一些註冊回調接口的方法。app

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;

  //...
  
}

 

 

二、Tween

Tween記錄了一個區間的begin和end。舉個例子來講:begin=100   end=200 ide

    • 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().源碼分析

 

三、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,由於它纔是動畫動起來的根本緣由(下面會講)。ui

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);
  }

 

 

四、CurvedAnimation

CurvedAnimation是一個非線性曲線的Animation,它繼承Animation,它與Animation的區別是在取值的時候按照特定非線性曲線函數生成的值。this

 

// 該函數的具體實現方式
  @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);
  }

 

 

五、SingleTickerProviderStateMixin

咱們建立動畫的時候,必需要傳遞一個TickerProvider參數,SingleTickerProviderStateMixin繼承TickerProvider。它自己有兩個做用:lua

    • 建立動畫計時器。
    • 關聯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();
  }
  //...
}

 

 

2、動畫動起來的過程

WechatIMG20.png

 

這時候咱們在外面經過anim.addListener(() {setState(() {});});不斷的視圖層進行重繪,則控件便動了起來。spa

 

 

3、總結

  • 抽象類Animation僅僅保存了動畫狀態和回調函數。
  • AnimationController和CurvedAnimation都繼承Animation。
  • AnimationController是動畫的控制器,控制動畫的開始和結束,接收動畫執行的時間。
  • Tween用於限定動畫的值的區間。
  • SingleTickerProviderStateMixin用於建立Ticker和綁定Widget生命週期。
  • Ticker是一個時間定時器,每個動畫幀每個動畫幀都會調用回調函數。

 

 

 

參考文獻:一、感謝Flutter中文網提供的資料。二、感謝簡書博主debug

相關文章
相關標籤/搜索