动画"/>
Flutter动画
Curve
缓和曲线,即单位区间到单位区间的映射,也就是动画在某一段时间内的变化特性,缓动曲线是用来随着时间的推移调整动画的变化率,允许它们加速或者减速,而不是以恒定的速度移动.
缓动曲线的映射当0.0必须映射到0.0上,1.0必须映射到1.0上。
这个类是一个抽象类,下面是它的部分源码:
@immutable
abstract class Curve {const Curve();double transform(double t) {assert(t >= 0.0 && t <= 1.0);if (t == 0.0 || t == 1.0) {return t;}return transformInternal(t);}@protecteddouble transformInternal(double t) {throw UnimplementedError();}Curve get flipped => FlippedCurve(this);@overrideString toString() {return '$runtimeType';}
}
通过上面的源码可以发现,具体的计算方法其实是在transformInternal(double t)
中,但是Curve
并没有实现这个方法,因此在具体使用Curve
的时候都是使用它的子类或者自己继承这个类来实现想要的效果
构造方法 const Curve();
抽象的const构造函数,这个构造函数允许子类提供const构造函数,以便可以在const
表达式中使用.
字段 flipped
返回与此曲线相反的新曲线。这对于CurvedAnimation.reverseCurve
通常是有用的。
方法 double transform(double t)
返回曲线在点t处的值,这个方法必须首先确保下面两项成立:
- td的值必须介于0.0和1.0之间
- t在0.0和1.0处的映射必须分别对应于0.0和1.0.
建议子类覆盖transformInternal
方法而不是这个函数,因为上面的情况已经在transform
的默认实现中处理过了,它将剩余逻辑委托给transformInternal
。
方法 double transformInternal(double t)
在0.0 < t < 1.0的情况下,返回曲线在点t处的值。
演示:将之前的一个点的运动轨迹使用Curve
进行处理,效果如下:
//动画速率变化CurvedAnimation _curvedAnimation;@overridevoid initState() {super.initState();//初始化动画控制器_animationController =AnimationController(duration: Duration(seconds: 3), vsync: this);//_animation = _tween.animate(_animationController);_curvedAnimation = CurvedAnimation(parent: _animationController,curve: Curves.bounceIn.flipped);_animation = _tween.animate(_curvedAnimation);_animation.addListener(() {_updatePage();});}
最终的效果如下:
[外链图片转存失败(img-jQVhBYK0-1564037739637)( “使用Curve包装动画”)]
Animation class
类型为T的动画,动画由一个值(类型为T)和一个状态组成,状态指示动画是在概念上从开始运行到结束还是从结束运行到开始,动画的实际值可能不会单调地变化(例如:动画使用一条反弹地曲线)。动画还允许其它对象监听其值或者状态的改变,These callbacks are called during the “animation” phase of the pipeline,就在重构widget之前。要创建一个可以向前或者向后运行的新动画,可以使用AnimationController
。
源码如下:
import 'package:flutter/foundation.dart';import 'tween.dart';// Examples can assume:
// AnimationController _controller;/// The status of an animation
enum AnimationStatus {/// The animation is stopped at the beginningdismissed,/// The animation is running from beginning to endforward,/// The animation is running backwards, from end to beginningreverse,/// The animation is stopped at the endcompleted,
}typedef AnimationStatusListener = void Function(AnimationStatus status);abstract class Animation<T> extends Listenable implements ValueListenable<T> {const Animation();@overridevoid addListener(VoidCallback listener);@overridevoid removeListener(VoidCallback listener);void addStatusListener(AnimationStatusListener listener);void removeStatusListener(AnimationStatusListener listener);AnimationStatus get status;@overrideT get value;bool get isDismissed => status == AnimationStatus.dismissed;bool get isCompleted => status == AnimationStatuspleted;@optionalTypeArgsAnimation<U> drive<U>(Animatable<U> child) {assert(this is Animation<double>);return child.animate(this as dynamic); // TODO(ianh): Clean this once is fixed.}@overrideString toString() {return '${describeIdentity(this)}(${toStringDetails()})';}String toStringDetails() {assert(status != null);String icon;switch (status) {case AnimationStatus.forward:icon = '\u25B6'; // >break;case AnimationStatus.reverse:icon = '\u25C0'; // <break;case AnimationStatuspleted:icon = '\u23ED'; // >>|break;case AnimationStatus.dismissed:icon = '\u23EE'; // |<<break;}assert(icon != null);return '$icon';}
}
属性 isCompleted -> bool
判断是否在动画结束时停止,从源码中可以看出,当动画的状态为AnimationStatuspleted
时返回true。
属性 isDismissed -> bool
判断是否这个动画在开始位置停止。从源码可以看出,当动画状态为AnimationStatus.dismissed
时返回true。
属性 AnimationStatus status
表示动画当前的状态,返回一个美剧类型AnimationStatus
中的一种状态。
属性 T value
动画的当前值,源码中即返回T。
方法 addListener(VoidCallback listener)
每当动画的值发生变化时调用这个监听器。添加的监听器可以通过方法removeListener
移除。
方法 addStatusListener(AnimationStatusListener listener)
每次动画状态改变的时候会调用这个监听器,可以使用removeStatusListener
来移除掉添加的监听器.
方法 Animation<U> drive <U>(Animatable<U> child)
将Tween
或者CurveTween
链接到此动画,但是需要注意的是:此方法仅适用于Animation实例,也就是说,它可以调用AnimationController
对象,以及CurvedAnimations
,ProxyAnimations
,ReverseAnimations
,TrainHoppingAnimations
等。它返回与方法(子方法)的参数U
类型相同的动画,方法(子方法)的值是通过将给定的Tween
应用于该动画的值而派生出来的.
示例一: 给定一个AnimationController _controller
,下面的代码创建一个Animation<Alignment>
,当控制器从0.0到1.0时,它从左上角到右上角摇摆
Animation<Alignment> _alignment1 = _controller.drive(AlignmentTween(begin: Alignment.topLeft,end: Alignment.topRight),
);
实例二:_alignment.value
可以在widget
的build
方法之后使用,例如:使用Alignment widget
定位子部件,使子部件的位置从左上角移动到右上角,curve
这种曲线是很常见的,例如在开始时使过渡变慢,在结束时使过渡变快,下面的代码演示了一种将前一个示例中的Tween
链接到Curve
(这里是Curves.easeIn
)的方法,在这里,Tween
是在其它地方创建的,作为一个可重用的变量,因为它的参数没有发生变化:
final Animatable<Alignment> _tween = AlignmentTween(begin:Alignment.topLeft,end:Alignment.topRight).chain(CurveTween(curve:Curves.easeIn));//...Animation<Alignment> _alignment2 = _controller.drive(_tween);
实例三:下面的代码和上面的是完全相同的,并且使用下面这种方式在以内联方式创建渐变时更清晰,当渐变的值依赖于其它变量时,这可能是首选:
Animation<Alignment> alignment3 = _controller.drive(CurveTween(curve:Curves.easeIn)).drive(AlignmentTween(begin:Alignment.topLeft,end:Alignment.topRight,));
AnimationController class
动画控制器.使用这个类可以执行以下任务:
- 使用
forward
或者reverse
播放一个动画,或者stop
停止一个动画 - 将动画设置为特定的值
- 定义动画的上界和下界值
- 使用物理模拟创建一个动画效果
默认情况下,AnimationController
在给定的持续时间内线性生成范围从0.0到1.0的值,每当运行应用程序的设备准备显示新帧时,动画控制器就会生成一个新值(通常,这个速度约为每秒60个值)。
一个AnimationController
需要一个TickerProvider
,通过构造方法中的vsync
参数进行配置。TickerProvider
接口描述了一个用于Ticker
对象的工厂,Ticker
是一个对象,它知道如何在SchedulerBinding
中注册自己,并在每一帧中触发一个回调,AnimationController
类使用一个Ticker
来遍历它所控制的动画。
如果一个AnimationContrioller
是在State
中被创建的,那么State
可以使用TickerProviderStateMixin
和SingleTickerProviderStateMixin
类来实现TickerProvider
接口,TickerProviderStateMixin
类总是为此而工作,在类只需要一个Ticker
的情况下(例如:如果类在整个声明周期中只创建一个AnimationController
),SingleTickerProviderStateMixin
效率稍微会高一点。
当一个AnimationController
不再需要的时候,应该处理掉它,这可以减稍泄露的可能性。当一个AnimationController
和StatefulWidget
一起使用时,通常会在State.initState
方法中创建它,然后在State.dispose
方法中处理掉它。
启动动画的方法当动画成功完成时返回一个TickerFuture
对象,并且不会抛出错误,如果一个动画被取消了,那么future
永远不会完成,这个对象也有一个TickerFuture.orCancel
属性,该属性返回一个future
,当动画成功完成时,该future
会完成,当动画终止时,该future
会以一个错误完成.
演示代码如下:
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';class AnimationDemo1 extends StatefulWidget { @override_AnimationDemo1State createState() {return _AnimationDemo1State();}
}class _AnimationDemo1State extends State<AnimationDemo1>with SingleTickerProviderStateMixin {//动画Animation<Alignment> _animation;//动画控制器AnimationController _animationController;@overridevoid initState() {super.initState();_initAnimation();}//初始化动画的相关信息void _initAnimation() {_animationController = AnimationController(vsync: this,duration: Duration(seconds: 3),);_animation = _animationController.drive(CurveTween(curve: Curves.bounceIn)).drive(Tween<Alignment>(begin: Alignment.bottomCenter, end: Alignment.topCenter));_animation.addListener(() {_updatePage();});}//更新页面void _updatePage() {if (mounted) {setState(() {});}}@overrideWidget build(BuildContext context) {return Container(constraints: BoxConstraints.tightForFinite(),alignment: Alignment.bottomCenter,child: Column(children: <Widget>[Container(constraints: BoxConstraints.expand(height: 300,width: 40),alignment: _animation.value,child: Container(constraints: BoxConstraints.expand(width: 30,height: 30.0),color: Colors.redAccent,),),RaisedButton(onPressed: (){fadeOutAndUpdateState();},child: Text("点击启动动画"),)],),);}@overridevoid dispose() {if (_animationController != null) {_animationController.dispose();}super.dispose();}Future<void> fadeOutAndUpdateState() async {try {await _animationController.forward().orCancel;await _animationController.reverse().orCancel;print("动画执行结束");} on TickerCanceled {print("捕获到TickerCanceled -- 动画被取消");}}
}
在上面的代码中,首先通过给AnimationDemo1State with SingleTickerProviderStateMixin
使当前类实现了TickerProvider
接口,然后定义了Animation
和AnimationController
对象,接着通过使用AnimationController
的drive
方法添加了Tween
和Curve
,最后给Animation
对象添加监听以改变页面进行重新绘制。同时,将启动动画的方法放在了fadeOutAndUpdateState() async
中,启动动画时使用了AnimationController.forward().orCancel
的方式,用以让动画在适当的时候结束。
程序的执行顺序如下:
- 点击启动动画,动画会首先正向执行,同时打印" 画执行状态改变:AnimationStatus.forward"
- 正向执行结束后,会首先打印"动画执行状态改变:AnimationStatuspleted",然后动画会反向执行,同时打印"动画执行状态改变:AnimationStatus.reverse"
- 反向执行结束后,会首先打印"动画执行状态改变:AnimationStatus.dismissed",接着会打印动画执行结束
- 动画执行过程中结束页面,会打印"捕获到TickerCanceled – 动画被取消"
执行过程如下所示:
打印信息如下:
I/flutter ( 9327): 动画执行状态改变:AnimationStatus.forward
I/flutter ( 9327): 动画执行状态改变:AnimationStatuspleted
I/flutter ( 9327): 动画执行状态改变:AnimationStatus.reverse
I/flutter ( 9327): 动画执行状态改变:AnimationStatus.dismissed
I/flutter ( 9327): 动画执行结束
I/flutter ( 9327): 动画执行状态改变:AnimationStatus.forward
V/DartMessenger( 9327): Sending message with callback over channel 'flutter/keyevent'
V/DartMessenger( 9327): Sending message with callback over channel 'flutter/keyevent'
V/NavigationChannel( 9327): Sending message to pop route.
V/DartMessenger( 9327): Sending message with callback over channel 'flutter/navigation'
I/flutter ( 9327): 捕获到TickerCanceled -- 动画被取消
总结
Flutter的动画逻辑相对来说还是比较清晰的,AnimationController
用于对动画的控制,开始,结束,反向播放等,Curve
用于描述动画的过程特性,加速,减速等,Animatable
或者Tween
及其子类用于包装需要的最终的结果类型,由于AnimationController
一般是从0.0到1.0之间变化,因此在Tween
中需要通过设置begin
属性和end
属性来进行映射。Animation
就是最终得到的动画对象,通过将不同时刻Animation
的value
值赋值给不同的对象来实现让对象动起来的效果。
更多推荐
Flutter动画
发布评论