缓和曲线,即单位区间到单位区间的映射,也就是动画在某一段时间内的变化特性,缓动曲线是用来随着时间的推移调整动画的变化率,允许它们加速或者减速,而不是以恒定的速度移动.
缓动曲线的映射当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);
}
@protected
double transformInternal(double t) {
throw UnimplementedError();
}
Curve get flipped => FlippedCurve(this);
@override
String toString() {
return '$runtimeType';
}
}
通过上面的源码可以发现,具体的计算方法其实是在transformInternal(double t)
中,但是Curve
并没有实现这个方法,因此在具体使用Curve
的时候都是使用它的子类或者自己继承这个类来实现想要的效果
抽象的const构造函数,这个构造函数允许子类提供const构造函数,以便可以在const
表达式中使用.
返回与此曲线相反的新曲线。这对于CurvedAnimation.reverseCurve
通常是有用的。
返回曲线在点t处的值,这个方法必须首先确保下面两项成立:
建议子类覆盖transformInternal
方法而不是这个函数,因为上面的情况已经在transform
的默认实现中处理过了,它将剩余逻辑委托给transformInternal
。
在0.0 < t < 1.0的情况下,返回曲线在点t处的值。
演示:将之前的一个点的运动轨迹使用Curve
进行处理,效果如下:
//动画速率变化
CurvedAnimation _curvedAnimation;
@override
void 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)(https://note.youdao.com/yws/public/resource/a9e390468cf5249223ed23567fcd1b5c/xmlnote/1E2D41742D5F4E768900628474C3C16A/14368 “使用Curve包装动画”)]
类型为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 beginning
dismissed,
/// The animation is running from beginning to end
forward,
/// The animation is running backwards, from end to beginning
reverse,
/// The animation is stopped at the end
completed,
}
typedef AnimationStatusListener = void Function(AnimationStatus status);
abstract class Animation extends Listenable implements ValueListenable {
const Animation();
@override
void addListener(VoidCallback listener);
@override
void removeListener(VoidCallback listener);
void addStatusListener(AnimationStatusListener listener);
void removeStatusListener(AnimationStatusListener listener);
AnimationStatus get status;
@override
T get value;
bool get isDismissed => status == AnimationStatus.dismissed;
bool get isCompleted => status == AnimationStatus.completed;
@optionalTypeArgs
Animation drive(Animatable child) {
assert(this is Animation);
return child.animate(this as dynamic); // TODO(ianh): Clean this once https://github.com/dart-lang/sdk/issues/32120 is fixed.
}
@override
String 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 AnimationStatus.completed:
icon = '\u23ED'; // >>|
break;
case AnimationStatus.dismissed:
icon = '\u23EE'; // |<<
break;
}
assert(icon != null);
return '$icon';
}
}
判断是否在动画结束时停止,从源码中可以看出,当动画的状态为AnimationStatus.completed
时返回true。
判断是否这个动画在开始位置停止。从源码可以看出,当动画状态为AnimationStatus.dismissed
时返回true。
表示动画当前的状态,返回一个美剧类型AnimationStatus
中的一种状态。
动画的当前值,源码中即返回T。
每当动画的值发生变化时调用这个监听器。添加的监听器可以通过方法removeListener
移除。
每次动画状态改变的时候会调用这个监听器,可以使用removeStatusListener
来移除掉添加的监听器.
Animation drive (Animatable child)
将Tween
或者CurveTween
链接到此动画,但是需要注意的是:此方法仅适用于Animation实例,也就是说,它可以调用AnimationController
对象,以及CurvedAnimations
,ProxyAnimations
,ReverseAnimations
,TrainHoppingAnimations
等。它返回与方法(子方法)的参数U
类型相同的动画,方法(子方法)的值是通过将给定的Tween
应用于该动画的值而派生出来的.
示例一: 给定一个AnimationController _controller
,下面的代码创建一个Animation
,当控制器从0.0到1.0时,它从左上角到右上角摇摆
Animation _alignment1 = _controller.drive(
AlignmentTween(
begin: Alignment.topLeft,
end: Alignment.topRight
),
);
实例二:_alignment.value
可以在widget
的build
方法之后使用,例如:使用Alignment widget
定位子部件,使子部件的位置从左上角移动到右上角,curve
这种曲线是很常见的,例如在开始时使过渡变慢,在结束时使过渡变快,下面的代码演示了一种将前一个示例中的Tween
链接到Curve
(这里是Curves.easeIn
)的方法,在这里,Tween
是在其它地方创建的,作为一个可重用的变量,因为它的参数没有发生变化:
final Animatable _tween = AlignmentTween(begin:Alignment.topLeft,end:Alignment.topRight).chain(CurveTween(curve:Curves.easeIn));
//...
Animation _alignment2 = _controller.drive(_tween);
实例三:下面的代码和上面的是完全相同的,并且使用下面这种方式在以内联方式创建渐变时更清晰,当渐变的值依赖于其它变量时,这可能是首选:
Animation alignment3 = _controller
.drive(CurveTween(curve:Curves.easeIn))
.drive(AlignmentTween(
begin:Alignment.topLeft,
end:Alignment.topRight,
));
动画控制器.使用这个类可以执行以下任务:
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
with SingleTickerProviderStateMixin {
//动画
Animation _animation;
//动画控制器
AnimationController _animationController;
@override
void initState() {
super.initState();
_initAnimation();
}
//初始化动画的相关信息
void _initAnimation() {
_animationController = AnimationController(
vsync: this,
duration: Duration(seconds: 3),
);
_animation = _animationController
.drive(CurveTween(curve: Curves.bounceIn))
.drive(Tween(
begin: Alignment.bottomCenter, end: Alignment.topCenter));
_animation.addListener(() {
_updatePage();
});
}
//更新页面
void _updatePage() {
if (mounted) {
setState(() {});
}
}
@override
Widget build(BuildContext context) {
return Container(
constraints: BoxConstraints.tightForFinite(),
alignment: Alignment.bottomCenter,
child: Column(
children: [
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(
"点击启动动画"
),
)
],
),
);
}
@override
void dispose() {
if (_animationController != null) {
_animationController.dispose();
}
super.dispose();
}
Future 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
的方式,用以让动画在适当的时候结束。
程序的执行顺序如下:
执行过程如下所示:
打印信息如下:
I/flutter ( 9327): 动画执行状态改变:AnimationStatus.forward
I/flutter ( 9327): 动画执行状态改变:AnimationStatus.completed
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
值赋值给不同的对象来实现让对象动起来的效果。