深入理解Flutter动画原理

一、概述

动画效果对于系统的用户体验非常重要,好的动画能让用户感觉界面更加顺畅,提升用户体验。

1.1 动画类型

Flutter动画大的分类来说主要分为两大类:

  • 补间动画:给定初值与终值,系统自动补齐中间帧的动画
  • 物理动画:遵循物理学定律的动画,实现了弹簧、阻尼、重力三种物理效果

在应用使用过程中常见动画模式:

  • 动画列表或者网格:例如元素的添加或者删除操作;
  • 转场动画Shared element transition:例如从当前页面打开另一页面的过渡动画;
  • 交错动画Staggered animations:比如部分或者完全交错的动画。

1.2 类图

核心类:

  • Animation对象是整个动画中非常核心的一个类;
  • AnimationController用于管理Animation;
  • CurvedAnimation过程是非线性曲线;
  • Tween补间动画
  • Listeners和StatusListeners用于监听动画状态改变。

AnimationStatus是枚举类型,有4个值;

取值解释dismissed动画在开始时停止forward动画从头到尾绘制reverse动画反向绘制,从尾到头completed动画在结束时停止

1.3 动画实例

<code> //[见小节2.1]
AnimationController animationController = AnimationController(
vsync: this, duration: Duration(milliseconds: 1000));
Animation animation = Tween(begin: 0.0,end: 10.0).animate(animationController);
animationController.addListener(() {
setState(() {});
});
//[见小节2.2]
animationController.forward();

/<code>

该过程说明:

  • AnimationController作为Animation子类,在屏幕刷新时生成一系列值,默认情况下从0到1区间的取值。
  • Tween的animate()方法来自于父类Animatable,该方法返回的对象类型为_AnimatedEvaluation,而该对象最核心的工作就是通过value来调用Tween的transform();

调用链:

<code>AnimationController.forward
AnimationController.\\_animateToInternal
AnimationController.\\_startSimulation
Ticker.start()
Ticker.scheduleTick()
SchedulerBinding.scheduleFrameCallback()
SchedulerBinding.scheduleFrame()
...
Ticker.\\_tick
AnimationController.\\_tick
Ticker.scheduleTick

/<code>

二、原理分析

2.1 AnimationController初始化

[-> lib/src/animation/animation_controller.dart]

<code>AnimationController({
double value,
this.duration,
this.debugLabel,
this.lowerBound = 0.0,
this.upperBound = 1.0,
this.animationBehavior = AnimationBehavior.normal,
@required TickerProvider vsync,
}) : _direction = _AnimationDirection.forward {
_ticker = vsync.createTicker(_tick); //[见小节2.1.1]
_internalSetValue(value ?? lowerBound); //[见小节2.1.3]
}

/<code>

该方法说明:

  • AnimationController初始化过程,一般都设置duration和vsync初值;
  • upperBound(上边界值)和lowerBound(下边界值)都不能为空,且upperBound必须大于等于lowerBound;
  • 创建默认的动画方向为向前(_AnimationDirection.forward);
  • 调用类型为TickerProvider的vsync对象的createTicker()方法来创建Ticker对象;

TickerProvider作为抽象类,主要的子类有SingleTickerProviderStateMixin和TickerProviderStateMixin,这两个类的区别就是是否支持创建多个TickerProvider,这里SingleTickerProviderStateMixin为例展开。

2.1.1 createTicker

[-> lib/src/widgets/ticker_provider.dart]

<code>mixin SingleTickerProviderStateMixin on State implements TickerProvider {
Ticker _ticker;

Ticker createTicker(TickerCallback onTick) {
//[见小节2.1.2]
_ticker = Ticker(onTick, debugLabel: 'created by $this');
return _ticker;
}

/<code>

2.1.2 Ticker初始化

[-> lib/src/scheduler/ticker.dart]

<code>class Ticker {
Ticker(this._onTick, { this.debugLabel }) {
}

final TickerCallback _onTick;
}

/<code>

将AnimationControllerd对象中的_tick()方法,赋值给Ticker对象的_onTick成员变量,再来看看该_tick方法。

2.1.3 _internalSetValue

[-> lib/src/animation/animation_controller.dart ::AnimationController]

<code>void _internalSetValue(double newValue) {
_value = newValue.clamp(lowerBound, upperBound);
if (_value == lowerBound) {
_status = AnimationStatus.dismissed;
} else if (_value == upperBound) {
_status = AnimationStatus.completed;
} else {
_status = (_direction == _AnimationDirection.forward) ?
AnimationStatus.forward :
AnimationStatus.reverse;
}
}

/<code>

根据当前的value值来初始化动画状态_status

2.2 forward

[-> lib/src/animation/animation_controller.dart ::AnimationController]

<code>TickerFuture forward({ double from }) {
//默认采用向前的动画方向
_direction = _AnimationDirection.forward;
if (from != null)
value = from;
return _animateToInternal(upperBound); //[见小节2.3]
}

/<code>

_AnimationDirection是枚举类型,有forward(向前)和reverse(向后)两个值,也就是说该方法的功能是指从from开始向前滑动,

2.3 _animateToInternal

[-> lib/src/animation/animation_controller.dart ::AnimationController]

<code>TickerFuture _animateToInternal(double target, { Duration duration, Curve curve = Curves.linear }) {
double scale = 1.0;
if (SemanticsBinding.instance.disableAnimations) {
switch (animationBehavior) {
case AnimationBehavior.normal:

scale = 0.05;
break;
case AnimationBehavior.preserve:
break;
}
}
Duration simulationDuration = duration;
if (simulationDuration == null) {
final double range = upperBound - lowerBound;
final double remainingFraction = range.isFinite ? (target - _value).abs() / range : 1.0;
//根据剩余动画的百分比来评估仿真动画剩余时长
simulationDuration = this.duration * remainingFraction;
} else if (target == value) {
//已到达动画终点,不再执行动画
simulationDuration = Duration.zero;
}
//停止老的动画[见小节2.3.1]
stop();
if (simulationDuration == Duration.zero) {
if (value != target) {
_value = target.clamp(lowerBound, upperBound);
notifyListeners();
}
_status = (_direction == _AnimationDirection.forward) ?
AnimationStatus.completed :
AnimationStatus.dismissed;
_checkStatusChanged();
//当动画执行时间已到,则直接结束
return TickerFuture.complete();
}
//[见小节2.4]
return _startSimulation(_InterpolationSimulation(_value, target, simulationDuration, curve, scale));
}

/<code>

默认采用的是线性动画曲线Curves.linear。

2.3.1 AnimationController.stop

<code>void stop({ bool canceled = true }) {
_simulation = null;
_lastElapsedDuration = null;

//[见小节2.3.2]
_ticker.stop(canceled: canceled);
}

/<code>

2.3.2 Ticker.stop

[-> lib/src/scheduler/ticker.dart]

<code>void stop({ bool canceled = false }) {
if (!isActive) //已经不活跃,则直接返回
return;

final TickerFuture localFuture = _future;
_future = null;
_startTime = null;

//[见小节2.3.3]
unscheduleTick();
if (canceled) {
localFuture._cancel(this);
} else {
localFuture._complete();
}
}

/<code>

2.3.3 Ticker.unscheduleTick

[-> lib/src/scheduler/ticker.dart]

<code>void unscheduleTick() {
if (scheduled) {
SchedulerBinding.instance.cancelFrameCallbackWithId(_animationId);
_animationId = null;
}
}

/<code>

2.3.4 _InterpolationSimulation初始化

[-> lib/src/animation/animation_controller.dart ::_InterpolationSimulation]

<code>class _InterpolationSimulation extends Simulation {
_InterpolationSimulation(this._begin, this._end, Duration duration, this._curve, double scale)

: _durationInSeconds = (duration.inMicroseconds * scale) / Duration.microsecondsPerSecond;

final double _durationInSeconds;
final double _begin;
final double _end;
final Curve _curve;
}

/<code>

该方法创建插值模拟器对象,并初始化起点、终点、动画曲线以及时长。这里用的Curve是线性模型,也就是说采用的是匀速运动。

2.4 _startSimulation

[-> lib/src/animation/animation_controller.dart]

<code>TickerFuture _startSimulation(Simulation simulation) {
_simulation = simulation;
_lastElapsedDuration = Duration.zero;
_value = simulation.x(0.0).clamp(lowerBound, upperBound);
//[见小节2.5]
final TickerFuture result = _ticker.start();
_status = (_direction == _AnimationDirection.forward) ?
AnimationStatus.forward :
AnimationStatus.reverse;
//[见小节2.4.1]
_checkStatusChanged();
return result;
}

/<code>

2.4.1 _checkStatusChanged

[-> lib/src/animation/animation_controller.dart]

<code>void _checkStatusChanged() {
final AnimationStatus newStatus = status;
if (_lastReportedStatus != newStatus) {
_lastReportedStatus = newStatus;
notifyStatusListeners(newStatus); //通知状态改变
}
}

/<code>

这里会回调_statusListeners中的所有状态监听器,这里的状态就是指AnimationStatus的dismissed、forward、reverse以及completed。

2.5 Ticker.start

[-> lib/src/scheduler/ticker.dart]

<code>TickerFuture start() {
_future = TickerFuture._();
if (shouldScheduleTick) {
scheduleTick(); //[见小节2.6]
}
if (SchedulerBinding.instance.schedulerPhase.index > SchedulerPhase.idle.index &&
SchedulerBinding.instance.schedulerPhase.index < SchedulerPhase.postFrameCallbacks.index)
_startTime = SchedulerBinding.instance.currentFrameTimeStamp;
return _future;
}

/<code>

此处的shouldScheduleTick等于!muted && isActive && !scheduled,也就是没有调度过的活跃状态才会调用Tick。

2.6 Ticker.scheduleTick

[-> lib/src/scheduler/ticker.dart]

<code>void scheduleTick({ bool rescheduling = false }) {
//[见小节2.7]
_animationId = SchedulerBinding.instance.scheduleFrameCallback(_tick, rescheduling: rescheduling);
}

/<code>

此处的_tick会在下一次vysnc触发时回调执行,见小节2.10。

2.7 scheduleFrameCallback

[-> lib/src/scheduler/binding.dart]

<code>int scheduleFrameCallback(FrameCallback callback, { bool rescheduling = false }) {
//[见小节2.8]

scheduleFrame();
_nextFrameCallbackId += 1;
_transientCallbacks[_nextFrameCallbackId] = _FrameCallbackEntry(callback, rescheduling: rescheduling);
return _nextFrameCallbackId;
}

/<code>

将前面传递过来的Ticker._tick()方法保存在_FrameCallbackEntry的callback中,然后将_FrameCallbackEntry记录在Map类型的_transientCallbacks,

2.8 scheduleFrame

[-> lib/src/scheduler/binding.dart]

<code>void scheduleFrame() {
if (_hasScheduledFrame || !_framesEnabled)
return;
ui.window.scheduleFrame();
_hasScheduledFrame = true;
}

/<code>

从文章Flutter之setState更新机制,可知此处调用的ui.window.scheduleFrame(),会注册vsync监听。当当下一次vsync信号的到来时会执行handleBeginFrame()。

2.9 handleBeginFrame

[-> lib/src/scheduler/binding.dart:: SchedulerBinding]

<code>void handleBeginFrame(Duration rawTimeStamp) {
Timeline.startSync('Frame', arguments: timelineWhitelistArguments);
_firstRawTimeStampInEpoch ??= rawTimeStamp;
_currentFrameTimeStamp = _adjustForEpoch(rawTimeStamp ?? _lastRawTimeStamp);
if (rawTimeStamp != null)
_lastRawTimeStamp = rawTimeStamp;
...

//此时阶段等于SchedulerPhase.idle;
_hasScheduledFrame = false;
try {
Timeline.startSync('Animate', arguments: timelineWhitelistArguments);
_schedulerPhase = SchedulerPhase.transientCallbacks;
//执行动画的回调方法

final Map callbacks = _transientCallbacks;
_transientCallbacks = {};
callbacks.forEach((int id, _FrameCallbackEntry callbackEntry) {
if (!_removedIds.contains(id))
_invokeFrameCallback(callbackEntry.callback, _currentFrameTimeStamp, callbackEntry.debugStack);
});
_removedIds.clear();
} finally {
_schedulerPhase = SchedulerPhase.midFrameMicrotasks;
}
}

/<code>

该方法主要功能是遍历_transientCallbacks,从前面小节[2.7],可知该过程会执行Ticker._tick()方法。

2.10 Ticker._tick

[-> lib/src/scheduler/ticker.dart]

<code>void _tick(Duration timeStamp) {
_animationId = null;
_startTime ??= timeStamp;
//[见小节2.11]
_onTick(timeStamp - _startTime);
//根据活跃状态来决定是否再次调度
if (shouldScheduleTick)
scheduleTick(rescheduling: true);
}

/<code>

该方法主要功能:

  • 小节[2.1.2]的Ticker初始化中,可知此处_onTick便是AnimationController的_tick()方法;
  • 小节[2.5]已介绍当仍处于活跃状态,则会再次调度,回到小节[2.6]的scheduleTick(),从而形成动画的连续绘制过程。

2.11 AnimationController._tick

[-> lib/src/animation/animation_controller.dart]

<code>void _tick(Duration elapsed) {
_lastElapsedDuration = elapsed;
//获取已过去的时长
final double elapsedInSeconds = elapsed.inMicroseconds.toDouble() / Duration.microsecondsPerSecond;
_value = _simulation.x(elapsedInSeconds).clamp(lowerBound, upperBound);
if (_simulation.isDone(elapsedInSeconds)) {
_status = (_direction == _AnimationDirection.forward) ?
AnimationStatus.completed :
AnimationStatus.dismissed;
stop(canceled: false); //当动画已完成,则停止
}
notifyListeners(); //通知监听器[见小节2.11.1]
_checkStatusChanged(); //通知状态监听器[见小节2.11.2]
}

/<code>

2.11.1 notifyListeners

[-> lib/src/animation/listener_helpers.dart ::AnimationLocalListenersMixin]

<code>void notifyListeners() {
final List<voidcallback> localListeners = List<voidcallback>.from(_listeners);
for (VoidCallback listener in localListeners) {
try {
if (_listeners.contains(listener))
listener();
} catch (exception, stack) {
...
}
}
}

/<voidcallback>/<voidcallback>/<code>

AnimationLocalListenersMixin的addListener()会向_listeners中添加监听器

2.11.2 _checkStatusChanged

[-> lib/src/animation/listener_helpers.dart ::AnimationLocalStatusListenersMixin]

<code>void notifyStatusListeners(AnimationStatus status) {
final List<animationstatuslistener> localListeners = List<animationstatuslistener>.from(_statusListeners);
for (AnimationStatusListener listener in localListeners) {
try {
if (_statusListeners.contains(listener))
listener(status);
} catch (exception, stack) {
...
}
}
}

/<animationstatuslistener>/<animationstatuslistener>/<code>

从前面的小节[2.4.1]可知,当状态改变时会调用notifyStatusListeners方法。AnimationLocalStatusListenersMixin的addStatusListener()会向_statusListeners添加状态监听器。

三、总结

3.1 动画流程图


深入理解Flutter动画原理



分享到:


相關文章: