动画
在任何系统的UI框架中,动画实现的原理都是相同的,即:
在一段时间内,快速地多次改变UI外观,由于人眼会产生视觉暂留,最终看到的就是一个“连续”的动画,这和电影的原理是一样的,而UI的一次改变称为一个动画帧,对应一次屏幕刷新,而决定动画流畅度的一个重要指标就是帧率FPS(Frame Per Second),指每秒的动画帧数。很明显,帧率越高则动画就会越流畅。
一般情况下,对于人眼来说,动画帧率超过16FPS,就比较流畅了,超过32FPS就会非常的细腻平滑,而超过32FPS基本就感受不到差别了。由于动画的每一帧都是要改变UI输出,所以在一个时间段内连续的改变UI输出是比较耗资源的,对设备的软硬件系统要求都较高,所以在UI系统中,动画的平均帧率是重要的性能指标,而在Flutter中,理想情况下是可以实现60FPS的,这和原生应用动画基本是持平的。
flutter中动画抽象
为了方便开发者创建动画,不同的UI系统对动画都进行了一些抽象,比如在Android中可以通过XML来描述一个动画然后设置给View。Flutter中也对动画进行了抽象,主要涉及Tween、Animation、Curve、Controller这些角色。
划重点
Animation对象是Flutter动画库中的一个核心类,它生成指导动画的值。
Animation对象知道动画的当前状态(例如,它是开始、停止还是向前或向后移动),但它不知道屏幕上显示的内容。
AnimationController管理Animation。
CurvedAnimation 将过程抽象为一个非线性曲线.
Tween在正在执行动画的对象所使用的数据范围之间生成值。例如,Tween可能会生成从红到蓝之间的色值,或者从0到255。
使用Listeners和StatusListeners监听动画状态改变。
- Flutter 中的动画系统基于Animation对象,widget可以在build 函数中对子widget进行属性赋值时取Animation对象的value,并可以监听动画的状态改变。
Animation<Double>
在Flutter中,Animation对象本身和UI渲染没有任何关系。Animation是一个抽象类,它拥有其当前值和状态(完成或停止)。其中一个比较常用的Animation类是Animation<double>。
Flutter中的Animation对象是一个在一段时间内依次生成一个区间之间值的类。Animation对象的输出可以是线性的、曲线的、一个步进函数或者任何其他可以设计的映射。 根据Animation对象的控制方式,动画可以反向运行,甚至可以在中间切换方向。
Animation还可以生成除double之外的其他类型值,如:Animation<Color> 或 Animation<Size>。
Animation对象有状态。可以通过访问其value属性获取动画的当前值。
Animation对象本身和UI渲染没有任何关系。
AnimationController
AnimationController是一个特殊的Animation对象,在屏幕刷新的每一帧,就会生成一个新的值。默认情况下,AnimationController在给定的时间段内会线性的生成从0.0到1.0的数字。
AnimationController派生自Animation<double>,因此可以在需要Animation对象的任何地方使用。 但是,AnimationController具有控制动画的其他方法。例如,.forward()方法可以启动动画。数字的产生与屏幕刷新有关,因此每秒钟通常会产生60个数字,在生成每个数字后,每个Animation对象调用添加的Listener对象。
AnimationController派生自Animation<double>,因此可以在需要Animation对象的任何地方使用。 但是,AnimationController具有控制动画的其他方法。例如,.forward()方法可以启动动画。数字的产生与屏幕刷新有关,因此每秒钟通常会产生60个数字,在生成每个数字后,每个Animation对象调用添加的Listener对象。
示例
- 直接用AnimationContrller生成动画
class _AnimationTestState extends State<AnimationTest> with SingleTickerProviderStateMixin{
Animation<double> _animation;
AnimationController _animationController;
@override
void initState() {
super.initState();
//lowerBound和upperBound值只能在0.0到1.0之间变化
_animationController = new AnimationController(vsync: this,lowerBound: 0.0,upperBound: 0.5,duration: new Duration(milliseconds: 3000));
// 添加监听
_animationController.addListener((){
setState(() { });
});
// 开启动画
_animationController.forward();
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: new AppBar(
title: new Text('animation'),
),
body: new Center(
child: new Container(
// 取AnimationController 对象value直接用
width: _animationController.value*300,
height: _animationController.value*300,
child: new FlutterLogo(),
),
),
);
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
}
Tween
- 默认情况下,AnimationController对象的范围从0.0到1.0。如果您需要不同的范围或不同的数据类型,则可以使用Tween来配置动画以生成不同的范围或数据类型的值。
- Tween是一个无状态(stateless)对象,需要begin和end值。Tween的唯一职责就是定义从输入范围到输出范围的映射。输入范围通常为0.0到1.0,但这不是必须的。
- Tween继承自Animatable<T>,而不是继承自Animation<T>。Animatable与Animation相似,不是必须输出double值。
- Tween对象不存储任何状态。相反,它提供了evaluate(Animation<double> animation)方法将映射函数应用于动画当前值。 Animation对象的当前值可以通过value()方法取到。evaluate函数还执行一些其它处理,例如分别确保在动画值为0.0和1.0时返回开始和结束状态。
- 要使用Tween对象,请调用其animate()方法,传入一个控制器对象。
示例:
class _AnimationTestState extends State<AnimationTest> with SingleTickerProviderStateMixin{
Animation<double> _animation;
AnimationController _animationController;
Tween<double> _sizeTween;
Tween<Color> _colorTween;
@override
void initState() {
super.initState();
_animationController = new AnimationController(vsync: this,duration: new Duration(milliseconds: 3000));
// _animation = new ColorTween(begin: Colors.transparent,end: Colors.amberAccent).animate(_animationController)
// ..addListener((){
// setState(() {
// });
// });
_animation = new Tween(begin: 0.0,end: 300.0).animate(_animationController)
..addListener((){
setState(() {
});
});
_animationController.forward();
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('animation'),
),
body: new Center(
child: new Container(
// color: _animation.value,
width: _animation.value,
height: _animation.value,
),
),
);
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
}
并行动画
示例
class _AnimationTestState extends State<AnimationTest> with SingleTickerProviderStateMixin{
Animation<double> _animation;
AnimationController _animationController;
Tween<double> _sizeTween;
Tween<Color> _colorTween;
@override
void initState() {
// TODO: implement initState
super.initState();
_animationController = new AnimationController(vsync: this,duration: new Duration(milliseconds: 3000));
_animation = new CurvedAnimation(parent: _animationController, curve: Curves.easeIn)
..addListener((){
setState(() {
});
});
_sizeTween = new Tween(begin: 0.0,end: 300.0);
_colorTween = new ColorTween(begin: Colors.transparent,end: Colors.amberAccent);
_animationController.forward();
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: new AppBar(
title: new Text('animation'),
),
body: new Center(
child: new Container(
color: _colorTween.evaluate(_animation),
width: _sizeTween.evaluate(_animation),
height: _sizeTween.evaluate(_animation),
),
),
);
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
}
- Curves 类定义常用动画曲线的数组,你可以通过CurvedAnimation 使用。
如果做改变大小或透明度的动画,可以直接用FadeTransition、SizeTransition。
示例
class _AnimationTestState extends State<AnimationTest> with SingleTickerProviderStateMixin{
AnimationController _animationController;
@override
void initState() {
// TODO: implement initState
super.initState();
_animationController = new AnimationController(vsync: this,duration: new Duration(milliseconds: 5000));
_animationController.forward();
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: new AppBar(
title: new Text('animation'),
),
body: new Center(
// child: new SizeTransition(
// sizeFactor: _animationController,
// child: new Container(
// color: Colors.amberAccent,
// width: 200.0,
// height: 200.0,
// ),
// ),
child: new FadeTransition(
opacity: _animationController,
child: new Container(
color: Colors.amberAccent,
width: 200.0,
height: 200.0,
),
),
),
);
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
}
AnimatedWidget
- AnimatedWidget使用来简化动画的组件,它封装了setState()的调用细节。省去了addListener()和setState().
示例
// 自定义widget继承AnimatedWidget
class AnimateLog extends AnimatedWidget{
AnimateLog({Key key,Animation<double> animation})
:super(key:key,listenable:animation);
@override
Widget build(BuildContext context) {
final Tween<double> tranTween = new Tween<double>(begin: 0.0,end: 300.0);
final Tween<double> opacTween = new Tween<double>(begin: 0.1,end: 1.0);
final Animation<double> animation = listenable;
// TODO: implement build
return new Opacity(
opacity: opacTween.evaluate(animation),
child: new Center(
child: new Container(
child: new FlutterLogo(),
width: tranTween.evaluate(animation),
height: tranTween.evaluate(animation),
),
),
);
}
}
- 使用
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: new AppBar(
title: new Text('animation'),
),
body: new Center(
child: new AnimateLog(animation: _animation)
),
);
}
AnimateBuilder
- 是为了把Widget从动画中剥离出来。
示例
class _AnimationTestState extends State<AnimationTest> with SingleTickerProviderStateMixin{
AnimationController _animationController;
Animation<double> _animation;
@override
void initState() {
super.initState();
_animationController = new AnimationController(vsync: this,duration: new Duration(milliseconds: 5000));
_animation = Tween(begin: 0.0,end: 300.0).animate(_animationController);
_animationController.forward();
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('animation'),
),
body: AnimatedBuilder(
animation: _animation,
child: new FlutterLogo(),
builder: (BuildContext context,Widget child){
return new Center(
child:new Container(
height: _animation.value,
width: _animation.value,
child: child,
)
);
},
)
);
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
}
- 可以单独封装一个动画,然后将Widget传进去,实现动画和Widget分离。
示例
class GrowTransition extends StatelessWidget{
final Animation<double> animation;
final Widget child;
GrowTransition({this.animation,this.child});
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
child: child,
animation: animation,
builder: (BuildContext context,Widget child){
return new Center(
child: new Container(
width: animation.value,
height: animation.value,
child: child,
),
);
},
);
}
}
- 使用
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: new AppBar(
title: new Text('animation'),
),
body: new GrowTransition(
child: new FlutterLogo(),
animation: _animation,
)
);
}
动画监听
- 可以通过Animation的addStatusListener() 来对动画的状态进行监听,以下是四种动画状态:
枚举值 | 含义 |
---|---|
dismissed | 动画在起始点停止 |
forward | 动画正在正向执行 |
reverse | 动画正在反向执行 |
completed | 动画在终点停止 |
示例
....
_animationController = new AnimationController(vsync: this,duration: new Duration(milliseconds: 5000));
_animation = Tween(begin: 0.0,end: 300.0).animate(_animationController);
_animation.addStatusListener((status){
if (status == AnimationStatus.completed){
_animationController.reverse();
}
else if (status == AnimationStatus.dismissed){
_animationController.forward();
}
});
_animationController.forward();
...
待续
网友评论