这个文件定义了
Flutter
很多的基础动画,主要是基于Container
的各个属性的动画封装,它的实现原理根据定义的时间曲线获取对应的补间值, 然后不断的更新widget的配置信息(如果有变动), 在vsync扫描时就会不断的更新每一帧的界面
连在一起看就成了动画效果, 实现它需要有时间函数曲线
,补间值
,vsync同步信号
1.视图更新的同步信号(Vsync)
Vsync信息由flutter engine提供, 当前的widget只需要
with
一个TickerProviderStateMixin
类, 就能监听到系统的vsync信号, 以下面的SingleTickerProvider
为栗子, 它直接控制和监听SchedulerBinding.instance
来实现vsync的回调事件转发给AnimationControllermixin SingleTickerProviderStateMixin<T extends StatefulWidget> on State<T> implements TickerProvider { //创建ticker,这里创建了一个tiker,而 `TickerProvider` 是个抽象类,所以vsync肯定和它有关
@override
Ticker createTicker(TickerCallback onTick) { ...
_ticker = Ticker(onTick, debugLabel: kDebugMode ? 'created by $this' : null);
@override
void didChangeDependencies() { ...
_ticker.muted = !TickerMode.of(context);
}
class Ticker {
//Ticker的初始化只有一个参数,没有继承其他的内
Ticker(this._onTick, { this.debugLabel }) {
//控制ticker的事件队列,确保次序正确
TickerFuture _future;
//决定当前ticker是否只是监听vsync不执行callback,避免视图不现实的内存开销
set muted(bool value) {
isMuted ...
unscheduleTick();
shouldScheduleTick ...
scheduleTick();
//开启一个定时刷新,如果当前能执行动画(!isMetued)则执行它的callback事件
TickerFuture start() {
...
if (shouldScheduleTick) {
scheduleTick();
...
return _future;
}
void stop({ bool canceled = false }) { ...
unscheduleTick();
if (canceled) {
localFuture._cancel(this);
} else {
localFuture._complete();
}
}
//记录animationId,避免重复schedule
@protected
bool get scheduled => _animationId != null;
//执行tick的三要素
@protected
bool get shouldScheduleTick => !muted && isActive && !scheduled;
//
void _tick(Duration timeStamp) {..
_onTick(timeStamp - _startTime);
if (shouldScheduleTick)
scheduleTick(rescheduling: true);
}
//最终将tick事件发送给 `SchedulerBinding.instance` ,持续定于window的每一帧
@protected
void scheduleTick({ bool rescheduling = false }) { ...
_animationId = SchedulerBinding.instance.scheduleFrameCallback(_tick, rescheduling: rescheduling);
}
@protected
void unscheduleTick() { ...
SchedulerBinding.instance.cancelFrameCallbackWithId(_animationId);
}
//用于接管另外一个ticker,释放原来的ticker,使用自己的ticker事件
void absorbTicker(Ticker originalTicker) {...
if (shouldScheduleTick)
scheduleTick();
originalTicker._future = null; // so that it doesn't get disposed when we dispose of originalTicker
originalTicker.unscheduleTick();
}
originalTicker.dispose();
}
2. 动画的时间曲线
- 通过Widget初始化时构造一个AnimationController, 它接收一个总的时常, 同是提供了ticker构造工厂函数, 而ticker可以监听vsync信号,
- 通过监听vsync事件和frameCallback的duration, 再结合动画的时间区间, 计算出每一帧绘制时的时间, 这样就生成一个动画的时间曲线
除了生成时间曲线以外, 它还提供了动画控制的能力, 比如重复开始, 反转, 每执行一个操作它都会去从新生成一个当前的时间值, 确保动画的进度按照代码设定的逻辑执行
class _AnimatedWidgetDemoState extends State<AnimatedWidgetDemo> with SingleTickerProviderStateMixin {
AnimationController _animationController;
@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: Duration(seconds: 2),
)..repeat();
}
//在AnimationController创建的时候,会创建ticker
class AnimationController ...
AnimationController({ ...
_ticker = vsync.createTicker(_tick);
...
}
void _tick(Duration elapsed) { ...
//事件比例计算,也就是当前的动画进度
_value = _simulation.x(elapsedInSeconds).clamp(lowerBound, upperBound) as double;
notifyListeners();
_checkStatusChanged();
}
//重复执行
TickerFuture repeat({ double min, double max, bool reverse = false, Duration period }) {
...
stop();
return _startSimulation(_RepeatingSimulation(_value, min, max, reverse, period, _directionSetter));
}
//开始执行动画
TickerFuture forward({ double from }) { ...
_direction = _AnimationDirection.forward;
return _animateToInternal(upperBound);
}
TickerFuture _animateToInternal(double target, { Duration duration, Curve curve = Curves.linear }) {...
stop(); ...
return _startSimulation(_InterpolationSimulation(_value, target, simulationDuration, curve, scale));
}
TickerFuture _startSimulation(Simulation simulation) { ...
_value = simulation.x(0.0).clamp(lowerBound, upperBound) as double;
final TickerFuture result = _ticker.start();
_checkStatusChanged();
return result;
}
//可以看到AnimationController对动画的控制最终都会传递给 `Ticker` ,它主要负责时间进度的计算,计算完毕后根据代码设定的逻辑通知ticker是否需要监听下一帧的回调事件。以下为
Flutter
定义的Curve
曲线,用于适配X
,Y
之间的比例关系ParametricCurve (curves.dart) _BottomSheetSuspendedCurve (scaffold.dart)
_BottomSheetSuspendedCurve (bottom_sheet.dart)
Curve2D (curves.dart)
CatmullRomSpline (curves.dart)
Curve (curves.dart)
FlippedCurve (curves.dart)
_Linear (curves.dart)
ElasticInOutCurve (curves.dart)
SawTooth (curves.dart)
Cubic (curves.dart)
ElasticOutCurve (curves.dart)
Interval (curves.dart)
ElasticInCurve (curves.dart)
_BounceInOutCurve (curves.dart)
CatmullRomCurve (curves.dart)
_BounceOutCurve (curves.dart)
Threshold (curves.dart)
_DecelerateCurve (curves.dart)
_BounceInCurve (curves.dart)
3. 补间值
- 它代表的是动画在每一帧渲染时, Widget(RenderObject的最终提交渲染信息)的属性的值, 简单的动画一般只有开始和结束2个值,需要由
时间曲线函数
和动画的起始值
共同作用来完成 - 动画过程也是线性的, 随着时间的推移, 按线性规律逐步完成的, 为了动画过渡的更加完美, 需要额外对补间值进行加工, 在保证时间因子是线性移动的前提下, 通过引入Curve函数, 每个时间节点的 属性值按照重新计算, 这样我们的补间值就变得丰富多样。
按照这个规律我们可以把线性的补间值变成各种变化规律的补间值
在flutter仓库的源码中定义了很多的动画补间值转换, 在flutter的动画设计中可以把它看作是一个以时间为
X
轴, 动画进度为Y
的函数, 函数的具体实现都封装在 transform方法中基于此规律我们可以定义自己Curve, 生成自己的曲线函数, 这样就能实时的调整动画的补间值, 以下为
Flutter
目前现有的一些Curve
,基本上满足了绝大部分应用场景。补间值的具体类, 基本上满足了绝大部分的场景, 直接就能使用
Animatable (tween.dart) Tween (tween.dart)
AlignmentTween (tweens.dart)
FractionalOffsetTween (tweens.dart)
AlignmentGeometryTween (tweens.dart)
RelativeRectTween (transitions.dart)
Matrix4Tween (implicit_animations.dart)
BoxConstraintsTween (implicit_animations.dart)
EdgeInsetsTween (implicit_animations.dart)
DecorationTween (implicit_animations.dart)
BorderTween (implicit_animations.dart)
BorderRadiusTween (implicit_animations.dart)
TextStyleTween (implicit_animations.dart)
EdgeInsetsGeometryTween (implicit_animations.dart)
ThemeDataTween (theme.dart)
_FontWeightTween (sliding_segmented_control.dart)
ShapeBorderTween (material.dart)
_InputBorderTween (input_decorator.dart)
MaterialPointArcTween (arc.dart)
ReverseTween (tween.dart)
StepTween (tween.dart)
SizeTween (tween.dart)
ColorTween (tween.dart)
ConstantTween (tween.dart)
IntTween (tween.dart)
RectTween (tween.dart)
MaterialRectCenterArcTween (arc.dart)
MaterialRectArcTween (arc.dart)
示例Demo
- 下面以最常用的
AnimatedContainer
为例, 通过它来构造一个动画, 下面是一个简易的Demo,
通过点击手势来触发ValueListenableBuilder重新构建, 切换动画的的初始值
AnimatedContainer
示例
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class AnimatedContainerDemo extends StatelessWidget {
final CheckStatusListener statusListener = CheckStatusListener(true);
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
statusListener.value = !statusListener.value;
},
child: ValueListenableBuilder<bool>(
valueListenable: statusListener,
builder: (BuildContext context, bool selected, Widget child) {
return Center(
child: AnimatedContainer(
selected ? 200.0 : 100.0,
height: selected ? 100.0 : 200.0,
color: selected ? Colors.red : Colors.blue,
alignment: selected
? Alignment.center
: AlignmentDirectional.topCenter,
duration: Duration(seconds: 2),
curve: Curves.fastOutSlowIn,
child: FlutterLogo(size: 75),
),
);
}),
);
}
}
class CheckStatusListener extends ValueNotifier<bool>
implements ValueListenable<bool> {
CheckStatusListener(bool isSelected) : super(isSelected);
}
AnimatedContainer代码实现
从构造方法就可以看出它支持设置多种不同的补间值
class AnimatedContainer extends ImplicitlyAnimatedWidget { AnimatedContainer({
Key key,
this.alignment,
this.padding,
Color color,
Decoration decoration,
this.foregroundDecoration,
double width,
double height,
BoxConstraints constraints,
this.margin,
this.transform,
this.child,
Curve curve = Curves.linear,
@required Duration duration,
VoidCallback onEnd,
})
...
class _AnimatedContainerState extends AnimatedWidgetBaseState<AnimatedContainer> {
AlignmentGeometryTween _alignment;
EdgeInsetsGeometryTween _padding;
DecorationTween _decoration;
DecorationTween _foregroundDecoration;
BoxConstraintsTween _constraints;
EdgeInsetsGeometryTween _margin;
Matrix4Tween _transform;
//它的父类也是一个空壳,只是包装了setState方法,这个方法会触发这个widget及children重建,慎用,不要将复杂过多的逻辑用这种方案时间,一般使用与小部件的widget,继续向上查找它的Ancestor
abstract class AnimatedWidgetBaseState<T extends ImplicitlyAnimatedWidget> extends ImplicitlyAnimatedWidgetState<T> {
@override
void initState() {
super.initState();
controller.addListener(_handleAnimationChanged);
}
void _handleAnimationChanged() {
setState(() { /* The animation ticked. Rebuild with new animation value */ });
}
}
//父类实现了tickerProvider协议,可以通过Tiker发送监听屏幕刷新事件,同时也持有了`AnimationController`用于控制基本的动画行为
abstract class ImplicitlyAnimatedWidgetState<T extends ImplicitlyAnimatedWidget> extends State<T> with SingleTickerProviderStateMixin<T> {
@protected
AnimationController get controller => _controller;
Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController( ...
_controller.addStatusListener((AnimationStatus status) { ...
_updateCurve(); //这里将 Curve函数和时间关联在一起,它和Curve将Tween关联在一起是一样的,因为最终都是通过3个数的乘积的到最终补间值
_constructTweens(); //初始化设置子类的Tween
didUpdateTweens(); //供子类在视图更新时刷新Tween
}
@override
void didUpdateWidget(T oldWidget) {
super.didUpdateWidget(oldWidget);
...
_updateCurve();
_controller.duration = widget.duration;
if (_constructTweens()) {
forEachTween((Tween<dynamic> tween, dynamic targetValue, TweenConstructor<dynamic> constructor) {
_updateTween(tween, targetValue);
return tween;
});
_controller
..value = 0.0
..forward();
didUpdateTweens();
}
}
//更新widget自带的curve函数,更新动画的时间进度,间接修改补间值
void _updateCurve() { ...
_animation = CurvedAnimation(parent: _controller, curve: widget.curve);
//自动更新补间动画值
void _updateTween(Tween<dynamic> tween, dynamic targetValue) {
..begin = tween.evaluate(_animation)
..end = targetValue;
}
//充分利多态的特性,实现子类Tween的构造,并执行
bool _constructTweens() {
bool shouldStartAnimation = false;
forEachTween((Tween<dynamic> tween, dynamic targetValue, TweenConstructor<dynamic> constructor) {
if (targetValue != null) {
tween ??= constructor(targetValue);
if (_shouldAnimateTween(tween, targetValue))
shouldStartAnimation = true;
} else {
tween = null;
}
return tween;
});
return shouldStartAnimation;
}
//子类通过重写这个方法
//1. 子类传递 `Tween<T> tween, T targetValue,`给父类,举个栗子,Tween<T> tween就是子类的 AlginmentTween,targetValue就是Aliginment.topLeft
//2. 父类调用`constructor`方法,子类生成对应的AlginmentTween,这样子类就持有了AlginmentTween,在子类的build方法中获取父类的`animation`,就能实时的改变Contianer相关的属性了(此处的tween回传给父类主要是为了检测动画进度)
//typedef TweenVisitor<T> = Tween<T> Function(Tween<T> tween, T targetValue, TweenConstructor<T> constructor);
@protected
void forEachTween(TweenVisitor<dynamic> visitor);
//用于hook子视图在widgetupdate时更新补间值
@protected
void didUpdateTweens() { }
}子类
_AnimatedContainerState
在每次构建中更新当前的补间值class _AnimatedContainerState extends AnimatedWidgetBaseState<AnimatedContainer> {... @override
Widget build(BuildContext context) {
return Container(
child: widget.child,
alignment: _alignment?.evaluate(animation),
padding: _padding?.evaluate(animation),
decoration: _decoration?.evaluate(animation),
foregroundDecoration: _foregroundDecoration?.evaluate(animation),
constraints: _constraints?.evaluate(animation),
margin: _margin?.evaluate(animation),
transform: _transform?.evaluate(animation),
);
}创建Tween的具体实现在父类初始化是触发
forEachTween
来创建子类的Tween``` dart abstract class ImplicitlyAnimatedWidgetState<T extends ImplicitlyAnimatedWidget> extends State<T> with SingleTickerProviderStateMixin<T> { ...
void initState() { ...
_constructTweens(); -> forEachTween((Tween<dynamic> tween, dynamic targetValue, TweenConstructor<dynamic> constructor) {
void didUpdateWidget(T oldWidget) {...
forEachTween((Tween<dynamic> tween, dynamic targetValue, TweenConstructor<dynamic> constructor) {
子类重写`forEachTween`,传入当前的`_alignmentTween`(父类动画进度检查用)和`widget.alignmentValue`
class _AnimatedContainerState extends AnimatedWidgetBaseState<AnimatedContainer> { ...
@override
void forEachTween(TweenVisitor<dynamic> visitor) {
_alignment = visitor(_alignment, widget.alignment, (dynamic value) =>
AlignmentGeometryTween(begin: value as AlignmentGeometry)) as AlignmentGeometryTween;//用于父类构造通过Tween开启动画用
...
}
```
小结
- 理解
AnimatedContainer
动画的封装主要是要了解forEachTween
的执行过程,它是一个嵌套函数,同时实现了父子协同工作的方式, AnimationController通过持有Ticker,可以很方便的注册和取消系统下一帧的回调,实时的计算当前的动画进度 动画的三要素:
Vsync
信号,视图的刷新周期Tween
: 动画的补间值TimeFunction
: 时间函数,动画在某个时刻内它的动画进度值
复杂的动画都是基于动画的 补间值和动画时间函数计算得来的.