Flutter之通過AnimationController源碼分析學(xué)習(xí)使用Animation

涉及到的類有

  • Animation
    • AnimationController
  • _ListenerMixin
    • AnimationEagerListenerMixin
    • AnimationLocalListenersMixin
    • AnimationLocalStatusListenersMixin
  • Simulation
    • SpringSimulation
    • _InterpolationSimulation
    • _RepeatingSimulation

1. 簡介

界面中,當(dāng)點擊一個按鈕,或者切換一個頁面,使用動畫進行過渡是再普通不過了,而今天,我們來學(xué)習(xí)一下Flutter的Animation吧!

2. AnimationController

繼承關(guān)系
Animation > AnimationController

在介紹Animation之前,首先我們看一下AnimationController

// 實現(xiàn)了三個Mixin 分別是eager渴望監(jiān)聽,local本地監(jiān)聽,localStatus本地狀態(tài)監(jiān)聽
class AnimationController extends Animation<double>
  with AnimationEagerListenerMixin, AnimationLocalListenersMixin, AnimationLocalStatusListenersMixin {

// value就是當(dāng)前動畫的值
// duration就是持續(xù)的時間
// debuglabel 就是用于識別該動畫的一個標(biāo)簽
// lowerBound 跟 upperBound就是動畫的值最大跟最小值
// vsync 可以理解為提供玩這個動畫的門票
  AnimationController({
    double value,
    this.duration,
    this.debugLabel,
    this.lowerBound = 0.0,
    this.upperBound = 1.0,
    @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);
  }

//該構(gòu)造方法沒有最大最小值,所以是無限范圍
  AnimationController.unbounded({
    double value = 0.0,
    this.duration,
    this.debugLabel,
    @required TickerProvider vsync,
  }) : assert(value != null),
       assert(vsync != null),
       lowerBound = double.negativeInfinity,//極小
       upperBound = double.infinity,//極大
       _direction = _AnimationDirection.forward {
    _ticker = vsync.createTicker(_tick);
    _internalSetValue(value);
  }
//最小值
  final double lowerBound;
//最大值
  final double upperBound;
//識別該動畫的一個標(biāo)簽
  final String debugLabel;

//獲取當(dāng)前對象
  Animation<double> get view => this;
//持續(xù)時間
  Duration duration;

//ticket門票
  Ticker _ticker;

//這里用于重新綁定門票
  void resync(TickerProvider vsync) {
    final Ticker oldTicker = _ticker;
    _ticker = vsync.createTicker(_tick);
    _ticker.absorbTicker(oldTicker);
  }

//模擬,這里解釋暫定
  Simulation _simulation;

  @override
  double get value => _value;
  double _value;
 
//這里設(shè)置值,設(shè)置值的時候會暫停動畫
  set value(double newValue) {
    assert(newValue != null);
    stop();
    _internalSetValue(newValue);
    notifyListeners();
    _checkStatusChanged();
  }

//重置為最小值
  void reset() {
    value = lowerBound;
  }

//獲取當(dāng)前動畫的速度,如果該動畫不是進行中,會返回0.0
  double get velocity {
    if (!isAnimating)
      return 0.0;
    return _simulation.dx(lastElapsedDuration.inMicroseconds.toDouble() / Duration.microsecondsPerSecond);
  }
//設(shè)置新的值后重新設(shè)置狀態(tài)
  void _internalSetValue(double newValue) {
    _value = newValue.clamp(lowerBound, upperBound);//判斷這個值,如果小于lower返回lower,大于upper返回upper
    if (_value == lowerBound) {
      _status = AnimationStatus.dismissed;
    } else if (_value == upperBound) {
      _status = AnimationStatus.completed;
    } else {
      _status = (_direction == _AnimationDirection.forward) ?
        AnimationStatus.forward :
        AnimationStatus.reverse;
    }
  }

//動畫經(jīng)過的時間,即是開始到現(xiàn)在,如果結(jié)束,返回null
  Duration get lastElapsedDuration => _lastElapsedDuration;
  Duration _lastElapsedDuration;

//動畫是否在運動
  bool get isAnimating => _ticker != null && _ticker.isActive;

//動畫的方向,前進還是后退
  _AnimationDirection _direction;

//當(dāng)前動畫的狀態(tài)
  @override
  AnimationStatus get status => _status;
  AnimationStatus _status;

//從from開始向前運動
  TickerFuture forward({ double from }) {
    assert(() {
      if (duration == null) {//如果持續(xù)時間為null會拋異常
        throw new FlutterError(
          'AnimationController.forward() called with no default Duration.\n'
          'The "duration" property should be set, either in the constructor or later, before '
          'calling the forward() function.'
        );
      }
      return true;
    }());
//設(shè)置狀態(tài)為向前運動
    _direction = _AnimationDirection.forward;
    if (from != null)
      value = from;//讓值等于from
    return _animateToInternal(upperBound);//開始運動到最大值
  }

//同上,方向為返回
  TickerFuture reverse({ double from }) {
    assert(() {
      if (duration == null) {
        throw new FlutterError(
          'AnimationController.reverse() called with no default Duration.\n'
          'The "duration" property should be set, either in the constructor or later, before '
          'calling the reverse() function.'
        );
      }
      return true;
    }());
    _direction = _AnimationDirection.reverse;
    if (from != null)
      value = from;
    return _animateToInternal(lowerBound);
  }

//動畫從當(dāng)前值運行到target目標(biāo)值
  TickerFuture animateTo(double target, { Duration duration, Curve curve = Curves.linear }) {
    _direction = _AnimationDirection.forward;
    return _animateToInternal(target, duration: duration, curve: curve);
  }
// 插值動畫
  TickerFuture _animateToInternal(double target, { Duration duration, Curve curve = Curves.linear }) {
    Duration simulationDuration = duration;
    if (simulationDuration == null) {
      assert(() {
        if (this.duration == null) {
          throw new FlutterError(
            'AnimationController.animateTo() called with no explicit Duration and no default Duration.\n'
            'Either the "duration" argument to the animateTo() method should be provided, or the '
            '"duration" property should be set, either in the constructor or later, before '
            'calling the animateTo() function.'
          );
        }
        return true;
      }());
//獲取范圍
      final double range = upperBound - lowerBound;
//當(dāng)前動畫還剩多少,百分比|range.isFinite 是否有限,即最大值或最小值是無窮就返回1.0,否則true
      final double remainingFraction = range.isFinite ? (target - _value).abs() / range : 1.0;
//持續(xù)時間*百分比,等于剩下的時間,這里duration為null,所以需要計算
      simulationDuration = this.duration * remainingFraction;
    } else if (target == value) {//如果目標(biāo)值等于當(dāng)前值,不運動
      // Already at target, don't animate.
      simulationDuration = Duration.zero;
    }
//先停止之前的動畫
    stop();
    if (simulationDuration == Duration.zero) {//如果時間為0
      if (value != target) {//當(dāng)前值不等于目標(biāo)值
        _value = target.clamp(lowerBound, upperBound);//判斷目標(biāo)值是否超過最大跟最小,然后賦值
        notifyListeners();//刷新監(jiān)聽
      }
//設(shè)置狀態(tài)
      _status = (_direction == _AnimationDirection.forward) ?
        AnimationStatus.completed ://完成upper
        AnimationStatus.dismissed;//取消lower
      _checkStatusChanged();//檢查值改變
      return new TickerFuture.complete();//返回已經(jīng)完成
    }
    assert(simulationDuration > Duration.zero);//判斷時間是否大于0
    assert(!isAnimating);//判斷是否不運動
    return _startSimulation(new _InterpolationSimulation(_value, target, simulationDuration, curve));//這里開始了屏幕的運動
  }
//重復(fù)動畫驅(qū)動,最大值,最小值,跟持續(xù)時間,可以實現(xiàn)永遠(yuǎn)不會完成的動畫
  TickerFuture repeat({ double min, double max, Duration period }) {
    min ??= lowerBound;
    max ??= upperBound;
    period ??= duration;
    assert(() {
      if (period == null) {
        throw new FlutterError(
          'AnimationController.repeat() called without an explicit period and with no default Duration.\n'
          'Either the "period" argument to the repeat() method should be provided, or the '
          '"duration" property should be set, either in the constructor or later, before '
          'calling the repeat() function.'
        );
      }
      return true;
    }());
    return animateWith(new _RepeatingSimulation(min, max, period));
  }

//臨界阻尼動畫
//動畫速度默認(rèn)為1.0
  TickerFuture fling({ double velocity = 1.0 }) {
//判斷方向
    _direction = velocity < 0.0 ? _AnimationDirection.reverse : _AnimationDirection.forward;
//目標(biāo)值,當(dāng)速度小于0的時候,方向為后退,targer為lowerBound的誤差值,因為速度改變了,會出現(xiàn)誤差
    final double target = velocity < 0.0 ? lowerBound - _kFlingTolerance.distance
                                         : upperBound + _kFlingTolerance.distance;
  //彈簧模擬|_kFlingSpringDescription為一個定制好的彈簧參數(shù)
    final Simulation simulation = new SpringSimulation(_kFlingSpringDescription, value, target, velocity)
      ..tolerance = _kFlingTolerance;//這里設(shè)置誤差進去
    return animateWith(simulation);
  }
//設(shè)置模擬
  TickerFuture animateWith(Simulation simulation) {
    stop();
    return _startSimulation(simulation);
  }
//開始模擬動畫
  TickerFuture _startSimulation(Simulation simulation) {
    assert(simulation != null);
    assert(!isAnimating);
//初始化值
    _simulation = simulation;
    _lastElapsedDuration = Duration.zero;
    _value = simulation.x(0.0).clamp(lowerBound, upperBound);
//門票開始
    final Future<Null> result = _ticker.start();
//根據(jù)方向設(shè)置狀態(tài)
    _status = (_direction == _AnimationDirection.forward) ?
      AnimationStatus.forward :
      AnimationStatus.reverse;
    _checkStatusChanged();
    return result;
  }

//停止動畫
  void stop({ bool canceled = true }) {
    _simulation = null;
    _lastElapsedDuration = null;
    _ticker.stop(canceled: canceled);
  }

//銷毀動畫
  @override
  void dispose() {
    assert(() {
      if (_ticker == null) {
        throw new FlutterError(
          'AnimationController.dispose() called more than once.\n'
          'A given $runtimeType cannot be disposed more than once.\n'
          'The following $runtimeType object was disposed multiple times:\n'
          '  $this'
        );
      }
      return true;
    }());
//門票撕掉
    _ticker.dispose();
    _ticker = null;
    super.dispose();
  }

//最后一次的狀態(tài)
  AnimationStatus _lastReportedStatus = AnimationStatus.dismissed;
//更新最后一次狀態(tài)
  void _checkStatusChanged() {
    final AnimationStatus newStatus = status;
    if (_lastReportedStatus != newStatus) {
      _lastReportedStatus = newStatus;
      notifyStatusListeners(newStatus);
    }
  }

//傳入經(jīng)過的時間,這里未使用,功效未名
  void _tick(Duration elapsed) {
    _lastElapsedDuration = elapsed;
//返回經(jīng)過多少秒
    final double elapsedInSeconds = elapsed.inMicroseconds.toDouble() / Duration.microsecondsPerSecond;
    assert(elapsedInSeconds >= 0.0);
//獲取動畫值
    _value = _simulation.x(elapsedInSeconds).clamp(lowerBound, upperBound);
//模擬動畫是否完成
    if (_simulation.isDone(elapsedInSeconds)) {
      _status = (_direction == _AnimationDirection.forward) ?
        AnimationStatus.completed :
        AnimationStatus.dismissed;
      stop(canceled: false);
    }
    notifyListeners();
    _checkStatusChanged();
  }

  @override
  String toStringDetails() {
    final String paused = isAnimating ? '' : '; paused';
    final String ticker = _ticker == null ? '; DISPOSED' : (_ticker.muted ? '; silenced' : '');
    final String label = debugLabel == null ? '' : '; for $debugLabel';
    final String more = '${super.toStringDetails()} ${value.toStringAsFixed(3)}';
    return '$more$paused$ticker$label';
  }
}

好了,有沒有人看完這段我加了注釋的AnimationController源碼呢?如果看完的話,大概對Animaiton這個類已經(jīng)了解了,現(xiàn)在讓我們試著去繼承Animation吧!

3. Animation

下面這段代碼繼承了Animation,可以發(fā)現(xiàn)需要重寫兩個監(jiān)聽的添加和移除,跟AnimationController有很大的出入,相同的只有 get status 跟 get value

class MyAnimationController extends Animation<double>{
  @override
  void addListener(listener) {
    // TODO: 添加監(jiān)聽
  }
  @override
  void addStatusListener(AnimationStatusListener listener) {
    // TODO: 添加狀態(tài)監(jiān)聽
  }
  @override
  void removeListener(listener) {
    // TODO: 移除監(jiān)聽
  }
  @override
  void removeStatusListener(AnimationStatusListener listener) {
    // TODO: 移除狀態(tài)監(jiān)聽
  }
  // TODO: 當(dāng)前狀態(tài)
  @override
  AnimationStatus get status => null;
  // TODO: 當(dāng)前值
  @override
  double get value => null;
}

4.揭開AnimationEagerListenerMixin, AnimationLocalListenersMixin,AnimationLocalStatusListenersMixin的面紗

1.AnimationEagerListenerMixin

繼承關(guān)系
_ListenerMixin > AnimationEagerListenerMixin

他實際上就是一個抽象類,在dart里面抽象類可繼承可實現(xiàn),看源碼知道,他主要的一個方法就是dispose,用于規(guī)定釋放資源的方法

///釋放此對象使用的資源。 該對象不再可用
///調(diào)用此方法后
  @mustCallSuper //該注解表示一定要調(diào)用super.dispose();
  void dispose() { }

2.AnimationLocalListenersMixin

繼承關(guān)系
_ListenerMixin > AnimationLocalListenersMixin

也是一個抽象類,但是該抽象類有自己的方法體,定義了一個_listeners,用于存放listener,該list是一個觀察者list

abstract class AnimationLocalListenersMixin extends _ListenerMixin {

  factory AnimationLocalListenersMixin._() => null;

  final ObserverList<VoidCallback> _listeners = new ObserverList<VoidCallback>();

//添加監(jiān)聽到list
  void addListener(VoidCallback listener) {
    didRegisterListener();
    _listeners.add(listener);
  }

//在list中移除監(jiān)聽
  void removeListener(VoidCallback listener) {
    _listeners.remove(listener);
    didUnregisterListener();
  }

//通知所有的監(jiān)聽器
  void notifyListeners() {
    final List<VoidCallback> localListeners = new List<VoidCallback>.from(_listeners);
    for (VoidCallback listener in localListeners) {
      try {
        if (_listeners.contains(listener))
          listener();
      } catch (exception, stack) {
        FlutterError.reportError(new FlutterErrorDetails(
          exception: exception,
          stack: stack,
          library: 'animation library',
          context: 'while notifying listeners for $runtimeType',
          informationCollector: (StringBuffer information) {
            information.writeln('The $runtimeType notifying listeners was:');
            information.write('  $this');
          }
        ));
      }
    }
  }
}

3.AnimationLocalStatusListenersMixin

繼承關(guān)系
_ListenerMixin > AnimationLocalListenersMixin

可以看出,這個抽象類跟AnimationLocalListenersMixin類似,只不過該抽象類負(fù)責(zé)跟status打交道,這里就不解析了

5. Simulation

該類主要定制動畫的運行過程,可以說相當(dāng)于Android中的動畫插值器

abstract class Simulation {
  Simulation({ this.tolerance = Tolerance.defaultTolerance });
//當(dāng)前的位置
  double x(double time);
//當(dāng)前的速度
  double dx(double time);
//是否完成
  bool isDone(double time);
//用于模糊的最大值跟最小值,接近某個值后算完成
  Tolerance tolerance;
}

先來看一下_InterpolationSimulation這個類

1. _InterpolationSimulation

class _InterpolationSimulation extends Simulation {
  _InterpolationSimulation(this._begin, this._end, Duration duration, this._curve)
    : assert(_begin != null),
      assert(_end != null),
      assert(duration != null && duration.inMicroseconds > 0),
      _durationInSeconds = duration.inMicroseconds / Duration.microsecondsPerSecond;
  final double _durationInSeconds;
  final double _begin;
  final double _end;
  final Curve _curve;
  @override
  double x(double timeInSeconds) {
//這里的動畫使用秒為單位,返回當(dāng)前進度
    final double t = (timeInSeconds / _durationInSeconds).clamp(0.0, 1.0);
//如果為開始就返回開始位置
    if (t == 0.0)
      return _begin;
//如果為結(jié)束就返回結(jié)束位置
    else if (t == 1.0)
      return _end;
//返回當(dāng)前位置 (開始+(結(jié)束-開始)*變化速率(進度))
    else
      return _begin + (_end - _begin) * _curve.transform(t);
  }
  @override
  double dx(double timeInSeconds) {
// 時間容差
    final double epsilon = tolerance.time;
//獲取到速率,可以計算得到為x  -_-!!
    return (x(timeInSeconds + epsilon) - x(timeInSeconds - epsilon)) / (2 * epsilon);
  }
  @override//判斷當(dāng)前時間是否大于約定時間為完成
  bool isDone(double timeInSeconds) => timeInSeconds > _durationInSeconds;
}

上面動畫從源碼得知,是一個勻加速的動畫效果

2._RepeatingSimulation

class _RepeatingSimulation extends Simulation {
  _RepeatingSimulation(this.min, this.max, Duration period)
    : _periodInSeconds = period.inMicroseconds / Duration.microsecondsPerSecond {
    assert(_periodInSeconds > 0.0);
  }
  final double min;
  final double max;
  final double _periodInSeconds;//時間/秒
  @override
  double x(double timeInSeconds) {
    assert(timeInSeconds >= 0.0);//時間需要大于0
    final double t = (timeInSeconds / _periodInSeconds) % 1.0;
//在兩個數(shù)字之間進行線性插值
    return ui.lerpDouble(min, max, t);
  }
  @override//線性運動
  double dx(double timeInSeconds) => (max - min) / _periodInSeconds;
  @override
  bool isDone(double timeInSeconds) => false;
}

未完待續(xù)??!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1、通過CocoaPods安裝項目名稱項目信息 AFNetworking網(wǎng)絡(luò)請求組件 FMDB本地數(shù)據(jù)庫組件 SD...
    陽明AI閱讀 16,172評論 3 119
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,777評論 25 709
  • 1、婚姻不用守 婚姻不是說要把另一半像守門員一樣守著,才能長久,長久的婚姻是基于,彼此互相欣賞、愛護,想要共度...
    清行qingxing閱讀 249評論 0 0
  • 總是旁觀。偶爾參與。 從今天到某一天。在路上。就不會是0。
    0401閱讀 211評論 0 0
  • 最近迷上熱縮片,準(zhǔn)備簡單說說教程。 1.工具 熱縮片教程,當(dāng)然首先就要有熱縮片。熱縮片有光滑和毛面之分,如果你買的...
    栗之雜貨店閱讀 89,808評論 15 40

友情鏈接更多精彩內(nèi)容