一、隐式(全自动)动画
所谓隐式动画就是只需要设置动画目标,过程控制由系统实现
一般是简单点的动画,比如只是简单的宽高变化。当然使用简单不代表功能就简单,下面会有体现
没有循环重播,不用随时中断,没有多方协调,就是从开始运行到结束。
这种动画一般就是隐式动画,使用flutter提供的api,隐式动画一般是Animated...开头。所以,便于学习记忆
还可以自定义隐式动画:TweenAnimationBuilder
AnimatedContainer
动画盒子,是作用到盒子属性上的,所以盒子有的一些属性,是可以动画效果的。。但是每个widget都有自己的管理,所以,如果想实现盒子里widget的动画就需要别的方式了
实现很简单,就两行代码,AnimatedContainer,duration变量。flutter会隐式的帮我们处理动画过程。
实现简单,但是功能可不简单,container有个decoration属性,该属性能实现的功能是很丰富的。所以,只要是盒子这个级别的所有变化都是可以动画的
不同控件间切换的过渡动画:AnimatedSwitcher
依然是个widget,是个盒子,但是该盒子是用于处理内部child组件切换时候动画的。
也是只有一个属性:duration。flutter会隐式帮我们处理动画过程
但是当子组件没有变化的时候,就没有动画了
比如:AnimatedSwitcher内的child组件是个text,text内容有变化的时候,就不会有动画,这是因为flutter判断child子组件没有发生变化,就没有触发动画过程
所以,只需要给组件设置一个key即可。可以如下简单的设置一下:UniqueKey,表示自己唯一的key
key: UniqueKey()
demo
return AnimatedContainer(
duration: const Duration(milliseconds: 300),
width: 200,
height: 300,
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 400),
//当内容有变化的时候就会触发动画
child: Text('content', key: UniqueKey(),),
// 丰富的动画控制
transitionBuilder: (child, animation) {
return FadeTransition(
opacity: animation,
child: ScaleTransition(
scale: animation,
child: child,
),
);
}
),
);
其他的一些可以自行尝试
一般就是Animated...开头的动画组件
- AnimatedOpacity
- AnimatedPadding
- ...
另外动画默认是线性变化的,动画有个curve属性,可以自行尝试
TweenAnimationBuilder
demo
return TweenAnimationBuilder(
tween: Tween(begin: 0.0, end: 1.0),
duration: const Duration(seconds: 2),
builder: (context, double value, widget) {
return Opacity(
opacity: value,
child: Container(
width: 200,
height: 300,
color: Colors.red,
),
);
}
);
补充:平移动画,offset可以直接写到tween里
二、显示(手动控制)动画
一般我们使用AnimationController的时候,直接在as中键入stanim使用模板代码:
class xxx extends StatefulWidget {
const xxx({Key? key}) : super(key: key);
@override
_xxxState createState() => _xxxState();
}
class _xxxState extends State<xxx> with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container();
}
}
vsync:垂直信号,由于不同设备刷新频率不一样,告诉系统何时同步动画状态。with SingleTickerProviderStateMixin。vsync:this即可
- controller是什么?
动画控制器,是跟系统有绑定的关系,通过vsync:this实现屏幕刷新回调builder回调。所以动画的实现最关心的是_controller持续的变动以及builder回调刷新,具体的动画控制可以自行实现,用Tween修改动画范围
比如我们使用RotationTransition的时候turns参数就可以传_controller,turns的入参就可以用AnimationController做一个映射,自己来控制转的圈数:turns:_controller.drive(Tween(end: 4))
这样实现了_controller的duration内旋转4圈
同理其他...Transition以及AnimatedTransition也一样可以这样用。比如FadeTransition,opacity就可以直接用_controller。
另外可以addListener监听它的值变化,也可以用.value获取动画过程中当前值
- turns传的controller怎么会是个double呢?
上面说的RotatationTransition,turns参数怎么可以用_controller呢?它是继承自extends Animation<double>的。
-
比如ScaleTransition,scale入参Tween(),可以用_controller.drive(Tween())包装为Animation<double>类型。作用就是_controller的duration范围内Tween补帧缩放范围.
比如AlignTransition,alignment入参是Animation<AlignmentGeometry>类型,也可以用_controller.drive()驱动
return SlideTransition(
position:_controller.drive(Tween(begin: Offset(0, 0), end: Offset(1, 1))),
);
return AlignTransition(
alignment: _controller.drive(Tween(begin: Alignment.topLeft, end: Alignment.bottomRight)),
child: const Text("d"),
);
- ticker是什么?
英文释义是嘀嗒声,形象的表述为屏幕刷新频率,屏幕要刷新一帧的时候就发一个tick
AnimationController初始化的时候设置的duration范围,有几种控制方式:
1、初始化controller时候,设置lowerBound,upperBound
2、scale:_controller.drive(Tween(begin:0.0, end:1.0))。
这种方式解决的是将Tween<double>转为Animation<double>
3、还有种写法:交错动画
Tween(begin:Offset(0, -0.5), end:Offset(0, 8))
.chain(CurveTween(curve: Curves.elasticInOut))
.chain(CurveTween(curve: Interval(0.0, 0.2)))
.animate(_controller)
chain可以理解为一个函数,就好比g(h(f(x))),函数嵌套,_controller就像是动画的持续过程,这个是跟屏幕刷新有关,但是可以通过多函数复合作用于实际动画效果。比如上面的代码:offset移动和移动速度曲线同时作用于动画过程,就有了从开始到结束沿移动速度曲线进行移动的动画效果
自定义显示动画:AnimatedBuidler
自定义显示动画,一是把动画控制交给AnimationController,二是可以做一些性能优化。比如把动画渲染过程中不需要重绘的组件交给AnimatedBuidler
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller,
//_controller控制内持续的调用builder
builder: (context, child) {
//每次调用builder,内部所有组件都会重新渲染,注意考虑优化性能
return Opacity(
//这里value超过1也没关系,有自动纠错机制。并不会报错。透明度是0~1,如果超过1就还是1不透明的效果
opacity: _controller.value,
child: Container(
width: 300,
height: 200 + 100 * _controller.value,
color: Colors.red,
child: child,
),
);
},
//动画执行过程中,不需要变化的部分可以传给AnimatedBuilder,
// 每次builder回调时候会再传出来,这样就避免了child部分的重新渲染
child: const Center(
child: Text(
"hello",
style: TextStyle(fontSize: 24),
),
),
);
}
三、其他动画
Flutter动画背后的机制和原理
Hero动画
动画转场更丝滑
但是貌似好像看不出hero动画的牛逼之处,下面再看个例子:
hero动画有个特点,就是触发那一刻就变为目标ui然后开始动画
功能强大,使用简单,只需要在开始和目标ui上包括Hero并设置相同唯一的tag即可
直接操作底层的CustomPaint
嵌入式lottie、Rive/Flare插件动画
体验了一下Rive在线动画平台,跟lottie是差不多的,支持的平台也很全。动画制作交给美工就行了,具体实现,都是加载json。提供的都有插件.
https://rive.app.可以体验一下,制作一个简单的动画,设置时间节点的关键帧,动画也有补帧动画的感觉