涉及到的类有
- Animation
- AnimationController
- _ListenerMixin
- AnimationEagerListenerMixin
- AnimationLocalListenersMixin
- AnimationLocalStatusListenersMixin
- Simulation
- SpringSimulation
- _InterpolationSimulation
- _RepeatingSimulation
1. 简介
界面中,当点击一个按钮,或者切换一个页面,使用动画进行过渡是再普通不过了,而今天,我们来学习一下Flutter的Animation吧!
2. AnimationController
继承关系
Animation > AnimationController
在介绍Animation之前,首先我们看一下AnimationController
// 实现了三个Mixin 分别是eager渴望监听,local本地监听,localStatus本地状态监听
class AnimationController extends Animation
with AnimationEagerListenerMixin, AnimationLocalListenersMixin, AnimationLocalStatusListenersMixin {
// value就是当前动画的值
// duration就是持续的时间
// debuglabel 就是用于识别该动画的一个标签
// 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);
}
//该构造方法没有最大最小值,所以是无限范围
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;
//识别该动画的一个标签
final String debugLabel;
//获取当前对象
Animation get view => this;
//持续时间
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;
//这里设置值,设置值的时候会暂停动画
set value(double newValue) {
assert(newValue != null);
stop();
_internalSetValue(newValue);
notifyListeners();
_checkStatusChanged();
}
//重置为最小值
void reset() {
value = lowerBound;
}
//获取当前动画的速度,如果该动画不是进行中,会返回0.0
double get velocity {
if (!isAnimating)
return 0.0;
return _simulation.dx(lastElapsedDuration.inMicroseconds.toDouble() / Duration.microsecondsPerSecond);
}
//设置新的值后重新设置状态
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;
}
}
//动画经过的时间,即是开始到现在,如果结束,返回null
Duration get lastElapsedDuration => _lastElapsedDuration;
Duration _lastElapsedDuration;
//动画是否在运动
bool get isAnimating => _ticker != null && _ticker.isActive;
//动画的方向,前进还是后退
_AnimationDirection _direction;
//当前动画的状态
@override
AnimationStatus get status => _status;
AnimationStatus _status;
//从from开始向前运动
TickerFuture forward({ double from }) {
assert(() {
if (duration == null) {//如果持续时间为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;
}());
//设置状态为向前运动
_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);
}
//动画从当前值运行到target目标值
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;
//当前动画还剩多少,百分比|range.isFinite 是否有限,即最大值或最小值是无穷就返回1.0,否则true
final double remainingFraction = range.isFinite ? (target - _value).abs() / range : 1.0;
//持续时间*百分比,等于剩下的时间,这里duration为null,所以需要计算
simulationDuration = this.duration * remainingFraction;
} else if (target == value) {//如果目标值等于当前值,不运动
// Already at target, don't animate.
simulationDuration = Duration.zero;
}
//先停止之前的动画
stop();
if (simulationDuration == Duration.zero) {//如果时间为0
if (value != target) {//当前值不等于目标值
_value = target.clamp(lowerBound, upperBound);//判断目标值是否超过最大跟最小,然后赋值
notifyListeners();//刷新监听
}
//设置状态
_status = (_direction == _AnimationDirection.forward) ?
AnimationStatus.completed ://完成upper
AnimationStatus.dismissed;//取消lower
_checkStatusChanged();//检查值改变
return new TickerFuture.complete();//返回已经完成
}
assert(simulationDuration > Duration.zero);//判断时间是否大于0
assert(!isAnimating);//判断是否不运动
return _startSimulation(new _InterpolationSimulation(_value, target, simulationDuration, curve));//这里开始了屏幕的运动
}
//重复动画驱动,最大值,最小值,跟持续时间,可以实现永远不会完成的动画
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));
}
//临界阻尼动画
//动画速度默认为1.0
TickerFuture fling({ double velocity = 1.0 }) {
//判断方向
_direction = velocity < 0.0 ? _AnimationDirection.reverse : _AnimationDirection.forward;
//目标值,当速度小于0的时候,方向为后退,targer为lowerBound的误差值,因为速度改变了,会出现误差
final double target = velocity < 0.0 ? lowerBound - _kFlingTolerance.distance
: upperBound + _kFlingTolerance.distance;
//弹簧模拟|_kFlingSpringDescription为一个定制好的弹簧参数
final Simulation simulation = new SpringSimulation(_kFlingSpringDescription, value, target, velocity)
..tolerance = _kFlingTolerance;//这里设置误差进去
return animateWith(simulation);
}
//设置模拟
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 result = _ticker.start();
//根据方向设置状态
_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();
}
//最后一次的状态
AnimationStatus _lastReportedStatus = AnimationStatus.dismissed;
//更新最后一次状态
void _checkStatusChanged() {
final AnimationStatus newStatus = status;
if (_lastReportedStatus != newStatus) {
_lastReportedStatus = newStatus;
notifyStatusListeners(newStatus);
}
}
//传入经过的时间,这里未使用,功效未名
void _tick(Duration elapsed) {
_lastElapsedDuration = elapsed;
//返回经过多少秒
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这个类已经了解了,现在让我们试着去继承Animation吧!
3. Animation
下面这段代码继承了Animation,可以发现需要重写两个监听的添加和移除,跟AnimationController有很大的出入,相同的只有 get status 跟 get value
class MyAnimationController extends Animation{
@override
void addListener(listener) {
// TODO: 添加监听
}
@override
void addStatusListener(AnimationStatusListener listener) {
// TODO: 添加状态监听
}
@override
void removeListener(listener) {
// TODO: 移除监听
}
@override
void removeStatusListener(AnimationStatusListener listener) {
// TODO: 移除状态监听
}
// TODO: 当前状态
@override
AnimationStatus get status => null;
// TODO: 当前值
@override
double get value => null;
}
4.揭开AnimationEagerListenerMixin, AnimationLocalListenersMixin,AnimationLocalStatusListenersMixin的面纱
1.AnimationEagerListenerMixin
继承关系
_ListenerMixin > AnimationEagerListenerMixin
他实际上就是一个抽象类,在dart里面抽象类可继承可实现,看源码知道,他主要的一个方法就是dispose,用于规定释放资源的方法
///释放此对象使用的资源。 该对象不再可用
///调用此方法后
@mustCallSuper //该注解表示一定要调用super.dispose();
void dispose() { }
2.AnimationLocalListenersMixin
继承关系
_ListenerMixin > AnimationLocalListenersMixin
也是一个抽象类,但是该抽象类有自己的方法体,定义了一个_listeners,用于存放listener,该list是一个观察者list
abstract class AnimationLocalListenersMixin extends _ListenerMixin {
factory AnimationLocalListenersMixin._() => null;
final ObserverList _listeners = new ObserverList();
//添加监听到list
void addListener(VoidCallback listener) {
didRegisterListener();
_listeners.add(listener);
}
//在list中移除监听
void removeListener(VoidCallback listener) {
_listeners.remove(listener);
didUnregisterListener();
}
//通知所有的监听器
void notifyListeners() {
final List localListeners = new List.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
继承关系
_ListenerMixin > AnimationLocalListenersMixin
可以看出,这个抽象类跟AnimationLocalListenersMixin类似,只不过该抽象类负责跟status打交道,这里就不解析了
5. Simulation
该类主要定制动画的运行过程,可以说相当于Android中的动画插值器
abstract class Simulation {
Simulation({ this.tolerance = Tolerance.defaultTolerance });
//当前的位置
double x(double time);
//当前的速度
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) {
//这里的动画使用秒为单位,返回当前进度
final double t = (timeInSeconds / _durationInSeconds).clamp(0.0, 1.0);
//如果为开始就返回开始位置
if (t == 0.0)
return _begin;
//如果为结束就返回结束位置
else if (t == 1.0)
return _end;
//返回当前位置 (开始+(结束-开始)*变化速率(进度))
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//判断当前时间是否大于约定时间为完成
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;
//在两个数字之间进行线性插值
return ui.lerpDouble(min, max, t);
}
@override//线性运动
double dx(double timeInSeconds) => (max - min) / _periodInSeconds;
@override
bool isDone(double timeInSeconds) => false;
}
未完待续!!