我们可能会需要一些复杂的动画,这些动画可能由一个动画序列或重叠的动画组成,比如:有一个柱状图,需要在高度增长的同时改变颜色,等到增长到最大高度后,我们需要在X轴上平移一段距离。可以发现上述场景在不同阶段包含了多种动画,要实现这种效果,使用交织动画(Stagger Animation)会非常简单。交织动画需要注意以下几点:
- 要创建交织动画,需要使用多个动画对象(
Animation
)。 - 一个
AnimationController
控制所有的动画对象。 - 给每一个动画对象指定时间间隔(Interval)(默认情况下 时间间隔是0.0~1.0)
所有动画都由同一个AnimationController驱动,无论动画需要持续多长时间,控制器的值必须在0.0到1.0之间,而每个动画的间隔(Interval)也必须介于0.0和1.0之间。对于在间隔中设置动画的每个属性,需要分别创建一个Tween用于指定该属性的开始值和结束值。也就是说0.0到1.0代表整个动画过程,我们可以给不同动画指定不同的起始点和终止点来决定它们的开始时间和终止时间。
示例1
实现一个透明度变化、大小变化、颜色变化、旋转的动画
class MSStaggerAnimationRouter1 extends StatefulWidget {
const MSStaggerAnimationRouter1({Key? key}) : super(key: key);
@override
State<MSStaggerAnimationRouter1> createState() =>
_MSStaggerAnimationRouter1State();
}
class _MSStaggerAnimationRouter1State extends State<MSStaggerAnimationRouter1>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> opacityAnimation;
late Animation<double> transformAnimation;
late Animation<double> sizeAnimation;
late Animation<Color?> colorAnimation;
@override
void initState() {
super.initState();
_controller =
AnimationController(vsync: this, duration: Duration(seconds: 5));
opacityAnimation = Tween(begin: 0.0, end: 1.0)
.animate(CurvedAnimation(parent: _controller, curve: Curves.linear));
transformAnimation = Tween(begin: 0.0, end: 2 * pi)
.animate(CurvedAnimation(parent: _controller, curve: Curves.linear));
sizeAnimation = Tween(begin: 0.0, end: 300.0)
.animate(CurvedAnimation(parent: _controller, curve: Curves.bounceIn));
colorAnimation = ColorTween(begin: Colors.amber, end: Colors.red).animate(
CurvedAnimation(parent: _controller, curve: Curves.decelerate));
_controller.forward();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Opacity(
opacity: opacityAnimation.value,
child: Transform(
transform: Matrix4.rotationZ(transformAnimation.value),
alignment: Alignment(0, 0), // 旋转中心点
child: Container(
width: sizeAnimation.value,
height: sizeAnimation.value,
color: colorAnimation.value,
),
),
);
},
),
),
);
}
@override
void dispose() {
super.dispose();
_controller.dispose();
}
}
示例2
实现一个柱状图增长的动画:
开始时高度从0增长到300像素,同时颜色由绿色渐变为红色;这个过程占据整个动画时间的60%。
高度增长到300后,开始沿X轴向右平移100像素;这个过程占用整个动画时间的40%。
class MSStaggerAnimationRouter2 extends StatefulWidget {
const MSStaggerAnimationRouter2({Key? key}) : super(key: key);
@override
State<MSStaggerAnimationRouter2> createState() =>
_MSStaggerAnimationRouter2State();
}
class _MSStaggerAnimationRouter2State extends State<MSStaggerAnimationRouter2>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> heightAnimation;
late Animation<Color?> colorAnimation;
late Animation<EdgeInsets?> paddingAnimation;
@override
void initState() {
super.initState();
_controller =
AnimationController(vsync: this, duration: Duration(seconds: 3));
// Interval 间隔,前60%的动画时间
heightAnimation = Tween(begin: 0.0, end: 300.0).animate(CurvedAnimation(
parent: _controller, curve: Interval(0.0, 0.6, curve: Curves.linear)));
colorAnimation = ColorTween(begin: Colors.green, end: Colors.red).animate(
CurvedAnimation(
parent: _controller,
curve: Interval(0.0, 0.6, curve: Curves.ease)));
// Interval 间隔,后40%的动画时间
paddingAnimation = Tween<EdgeInsets>(
begin: EdgeInsets.only(left: 0.0), end: EdgeInsets.only(left: 100))
.animate(
CurvedAnimation(parent: _controller, curve: Interval(0.6, 1.0)));
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
height: 300,
alignment: Alignment(0, 1),
child: AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Padding(
padding: paddingAnimation.value!,
child: Container(
height: heightAnimation.value,
width: 50,
color: colorAnimation.value,
),
);
},
),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.play_circle),
onPressed: () {
if (_controller.isAnimating) {
_controller.stop();
} else {
if (_controller.status == AnimationStatus.dismissed ||
_controller.status == AnimationStatus.forward) {
_controller.forward();
} else {
_controller.reverse();
}
}
},
),
);
}
@override
void dispose() {
super.dispose();
_controller.dispose();
}
}