概念理解
一个物体的属性包括:大小(Size),透明(Opacity),旋转(Rotation) 滑行(Slide)
动画是驱动器,动画属性,曲线快慢(时间间隔) ,多个动画公用一个驱动器的时候,可以让某一段时间运行某一个动画
驱动器负责心跳
曲线和动画属性共同提供动画当前的属性值
动画本质
1.改变Widget的属性值
2. 估值器 插值器 然后作用到controller上面,controller获取对应的值作用到Widget
3. 范围 快慢 时间 是否重复 获取对应的值来改变Widget
插值器/估值器
为了使得动画呈现出丰富的动画效果,就需要使用非线性动画,插值器与估值器可以解决这个问题。概念如下所示:
- 插值器:设置属性值从初始值过渡到结束值的变化规律,如匀速、加速及减速等等。即确定了动画效果变化的模式,如匀速变化、加速变化等等。主要应用于实现非线性运动的动画效果。
- 估值器:设置属性值从初始值过渡到结束值的变化具体数值。估值器的作用是协助插值器实现非线性运动的动画效果。插值器决定值的变化规律(匀速、加速),即决定的是变化趋势,而接下来的具体变化数值则交给估值器。如:动画进行了50%(初始值=100,结束值=200 ),那么匀速插值器计算出了当前属性值改变的百分比是50%,那么估值器则负责计算当前属性值 = 100 + (200-100)x50% = 150。插值器其实并不复杂,就是一个数学函数,设置属性值从初始值过渡到结束值的变化规律。每个平台都有自己定义好的一系列插值器,可以供开发者选择使用,也提供自定义的接口,本质上是一个贝塞尔函数。
Flutter中的动画类型
Flutter中动画分为两类,如下所示:
- 补间(Tween)动画:定义开始点、结束点、时间和速度等参数,然后由框架自动计算如何从开始点过度达到结束点。
- 基于物理的动画:模拟真实世界的行为。例如,当你掷球时,球在何处落地,取决于抛球速度有多快、球有多重、距离地面有多远。
动画 Animation
- Tween (start-end)补间对象,用于计算动画使用的数据范围之间的插值。
- AnimationStatus()
- Listeners和StatusListeners 用于监听动画状态改变
- object 属性
- AnimationController 控制器
- CurvedAnimation 用于定义非线性曲线动画
Tween (估值器 给出范围的值)
开始值和结束值之间的线性插值
如果你想在一个范围内做插值会很有用吗
_animation = _controller.drive( Tween<Offset>( begin: const Offset(100.0, 50.0), end: const Offset(200.0, 300.0), ), ); _animation = Tween<Offset>( begin: const Offset(100.0, 50.0), end: const Offset(200.0, 300.0), ).animate(_controller);
Tween 子类
子类 | – |
ReverseTween | 一个[补间],它以相反的方式计算它的[父] |
ColorTween | 两种颜色之间的插值,使用Color.lerp |
SizeTween | 两个尺寸之间的插值,使用Size.lerp |
RectTween | Rect.lerp |
IntTween | 两个整数之间的补全运算 |
StepTween | 两个整型之间的插值 |
ConstantTween | 具有常数值的补间 |
CurveTween | – |
AnimatedWidget | 当给定的[Listenable]更改值时重新构建的小部件 |
T lerp(double t) { assert(begin != null); assert(end != null); //返回值 = 开始值 + (结束值 - 开始值) * 传入值 return begin + (end - begin) * t;} @overrideT transform(double t) { //开始 if (t == 0.0) return begin; //结束 if (t == 1.0) return end; //中间值 return lerp(t);}
AnimatedWidget 对于没有附加状态的简单情况,可以考虑使用
class Spinner extends StatefulWidget { @override _SpinnerState createState() => _SpinnerState(); } class _SpinnerState extends State<Spinner> with TickerProviderStateMixin { AnimationController _controller; @override void initState() { super.initState(); _controller = AnimationController( duration: const Duration(seconds: 10), vsync: this, )..repeat(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return SpinningContainer(controller: _controller); } } class SpinningContainer extends AnimatedWidget { const SpinningContainer({Key key, AnimationController controller}) : super(key: key, listenable: controller); Animation<double> get _progress => listenable; @override Widget build(BuildContext context) { return Transform.rotate( angle: _progress.value * 2.0 * math.pi, child: Container(width: 200.0, height: 200.0, color: Colors.green), ); } }
AnimatedWidget 的子类
子类 | – |
RotationTransition | 旋转 |
SlideTransition | 动画小部件相对于其正常位置的位置 |
ScaleTransition | 缩放比例 |
SizeTransition | 动画自己的大小和剪辑,并对齐其子女 |
FadeTransition | 透明 |
RelativeRectTween | 两个相对矩形之间的内插 使用RelativeRect.lerp |
PositionedTransition | 只有当它是[Stack]的子元素时才有效 |
DecoratedBoxTransition | [DecoratedBox]的动画版本,它可以动画化[Decoration]的不同属性 |
AlignTransition | 使其[Align]具有动画效果的[Align]的动画版本。对齐]属性 |
DefaultTextStyleTransition | 画版本的[DefaultTextStyle],它的[TextStyle]的不同属性///动画 |
AnimatedBuilder | 用于构建动画的通用小部件 |
AnimatedBuilder 对于涉及其他状态的更复杂的情况,请考虑使用
class Spinner extends StatefulWidget { @override _SpinnerState createState() => _SpinnerState(); } class _SpinnerState extends State<Spinner> with SingleTickerProviderStateMixin { AnimationController _controller; @override void initState() { super.initState(); _controller = AnimationController( duration: const Duration(seconds: 10), vsync: this, )..repeat(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return AnimatedBuilder( animation: _controller, child: Container( width: 200.0, height: 200.0, color: Colors.green, child: const Center( child: Text('Wee'), ), ), builder: (BuildContext context, Widget child) { return Transform.rotate( angle: _controller.value * 2.0 * math.pi, child: child, ); }, ); } }
AnimationStatus
状态 | 说明 |
dismissed | 动画一开始就停止了 |
forward | 动画从头到尾都在播放 |
reverse | 动画从结束到开始倒着播放 |
completed | 动画在最后停止 |
Listenable
void addListener(VoidCallback listener);
void removeListener(VoidCallback listener);
void addStatusListener(AnimationStatusListener listener);
void removeStatusListener(AnimationStatusListener listener);
AnimationController
属性 | 说明 |
Duration | 这个动画应该持续多长时间 |
debugLabel | 是一个字符串,用于在///调试期间帮助识别此动画(由[toString]使用) |
lowerBound | 为该动画所能获得的最小值和///该动画被视为被取消的值。它不能是/// null。 |
upperBound | 是该动画可以获得的最大值和///被认为完成该动画的值。它不能是/// null。 |
TickerProvider | Ticker的提供者,Ticker的主要作用是每个动画帧调用一次它的回调,作用就显而易见了,相当于给动画添加了一个动起来的引擎 |
TickerFuture forward({ double from }) {}
TickerFuture reverse({ double from }) {}
TickerFuture repeat({ double min, double max, Duration period }) {}
TickerFuture fling({ double velocity: 1.0 }) {}
void stop({ bool canceled: true }) {}
void dispose() {}
class Foo extends StatefulWidget { Foo({ Key key, this.duration }) : super(key: key); final Duration duration; @override _FooState createState() => _FooState(); } class _FooState extends State<Foo> with SingleTickerProviderStateMixin { AnimationController _controller; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, // the SingleTickerProviderStateMixin //vsync传入是防止动画离屏之后继续消耗资源 duration: widget.duration, ); } @override void didUpdateWidget(Foo oldWidget) { super.didUpdateWidget(oldWidget); _controller.duration = widget.duration; } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Container(); } } Future<void> fadeOutAndUpdateState() async { try { await fadeAnimationController.forward().orCancel; await sizeAnimationController.forward().orCancel; setState(() { dismissed = true; }); } on TickerCanceled { // the animation got canceled, probably because we were disposed } }
CurvedAnimation(插值器 范围内值的变化快慢)
Curves 一组常见的动画缓动曲线
CurveTween 可以用来将[曲线]应用到[动画]中
final Animation<double> animation = _controller.drive( CurveTween(curve: Curves.ease), );
final Animation<double> animation = CurvedAnimation( parent: controller, curve: Curves.easeIn, reverseCurve: Curves.easeOut, );
TweenSequence
final Animation<double> animation = TweenSequence( <TweenSequenceItem<double>>[ TweenSequenceItem<double>( tween: Tween<double>(begin: 5.0, end: 10.0) .chain(CurveTween(curve: Curves.ease)), weight: 40.0, ), TweenSequenceItem<double>( tween: ConstantTween<double>(10.0), weight: 20.0, ), TweenSequenceItem<double>( tween: Tween<double>(begin: 10.0, end: 5.0) .chain(CurveTween(curve: Curves.ease)), weight: 40.0, ), ], ).animate(myAnimationController);
Animatable.animate 和 Animatable.chain
SingleTickerProviderStateMixin
vsync对象会绑定动画的定时器到一个可视的Widget,所以当Widget不显示时,动画定时器将会暂停,当Widget再次显示时,动画定时
器重新恢复执行,这样就可以避免动画相关UI不在当前屏幕时消耗资源。如果要使用自定义的State对象作为vsync时,请包含
TickerProviderStateMixin
动画流程
其他
参考文章
- Flutter动画详解
- flutter源码