交错动画

有些时候我们可能会需要一些负杂的动画,这些动画可能需要一个动画序列或重叠的动画组成,比如:有一个柱状图,需要在高度增长的同改变颜色,等到增长到最大高度后,我们需要在X轴上平移一段距离。这时我们就需要使用交错动画(Stagger Animation)。交错动画需要注意以下几点:

  1. 要创建交错动画,需要使用多个动画对象
  2. 一个AnimationController控制所有动画
  3. 给每一个动画对象指定间隔(Interval)

所有动画都由同一个AnimationController驱动,无论动画实时持续多长时间,控制器的值必须介于0.0和1.0之间,而每个动画的间隔(Interval)介于0.0和1.0之间。对于在间隔中设置动画的每个属性,请创建一个Tween。 Tween指定该属性的开始值和结束值。也就是说0.0到1.0代表整个动画过程,我们可以给不同动画指定起始点和终止点来决定它们的开始时间和终止时间。

示例

下面我们看一个例子,实现一个柱状图增长的动画:

  1. 开始时高度从0增长到300像素,同时颜色由绿色渐变为红色;这个过程占据整个动画时间的60%。
  2. 高度增长到300后,开始沿X轴向右平移100像素;这个过程占用整个动画时间的40%。

我们将执行动画的Widget分离出来:

  1. class StaggerAnimation extends StatelessWidget {
  2. StaggerAnimation({ Key key, this.controller }): super(key: key){
  3. //高度动画
  4. height = Tween<double>(
  5. begin:.0 ,
  6. end: 300.0,
  7. ).animate(
  8. CurvedAnimation(
  9. parent: controller,
  10. curve: Interval(
  11. 0.0, 0.6, //间隔,前60%的动画时间
  12. curve: Curves.ease,
  13. ),
  14. ),
  15. );
  16. color = ColorTween(
  17. begin:Colors.green ,
  18. end:Colors.red,
  19. ).animate(
  20. CurvedAnimation(
  21. parent: controller,
  22. curve: Interval(
  23. 0.0, 0.6,//间隔,前60%的动画时间
  24. curve: Curves.ease,
  25. ),
  26. ),
  27. );
  28. padding = Tween<EdgeInsets>(
  29. begin:EdgeInsets.only(left: .0),
  30. end:EdgeInsets.only(left: 100.0),
  31. ).animate(
  32. CurvedAnimation(
  33. parent: controller,
  34. curve: Interval(
  35. 0.6, 1.0, //间隔,后40%的动画时间
  36. curve: Curves.ease,
  37. ),
  38. ),
  39. );
  40. }
  41. final Animation<double> controller;
  42. Animation<double> height;
  43. Animation<EdgeInsets> padding;
  44. Animation<Color> color;
  45. Widget _buildAnimation(BuildContext context, Widget child) {
  46. return Container(
  47. alignment: Alignment.bottomCenter,
  48. padding:padding.value ,
  49. child: Container(
  50. color: color.value,
  51. width: 50.0,
  52. height: height.value,
  53. ),
  54. );
  55. }
  56. @override
  57. Widget build(BuildContext context) {
  58. return AnimatedBuilder(
  59. builder: _buildAnimation,
  60. animation: controller,
  61. );
  62. }
  63. }

StaggerAnimation中定义了三个动画,分别是对Container的height、color、padding属性设置的动画,然后通过Interval来为每个动画指定在整个动画过程的起始点和终点。

下面我们来实现启动动画的路由:

  1. class StaggerDemo extends StatefulWidget {
  2. @override
  3. _StaggerDemoState createState() => _StaggerDemoState();
  4. }
  5. class _StaggerDemoState extends State<StaggerDemo> with TickerProviderStateMixin {
  6. AnimationController _controller;
  7. @override
  8. void initState() {
  9. super.initState();
  10. _controller = AnimationController(
  11. duration: const Duration(milliseconds: 2000),
  12. vsync: this
  13. );
  14. }
  15. Future<Null> _playAnimation() async {
  16. try {
  17. //先正向执行动画
  18. await _controller.forward().orCancel;
  19. //再反向执行动画
  20. await _controller.reverse().orCancel;
  21. } on TickerCanceled {
  22. // the animation got canceled, probably because we were disposed
  23. }
  24. }
  25. @override
  26. Widget build(BuildContext context) {
  27. return GestureDetector(
  28. behavior: HitTestBehavior.opaque,
  29. onTap: () {
  30. _playAnimation();
  31. },
  32. child: Center(
  33. child: Container(
  34. width: 300.0,
  35. height: 300.0,
  36. decoration: BoxDecoration(
  37. color: Colors.black.withOpacity(0.1),
  38. border: Border.all(
  39. color: Colors.black.withOpacity(0.5),
  40. ),
  41. ),
  42. //调用我们定义的交错动画Widget
  43. child: StaggerAnimation(
  44. controller: _controller
  45. ),
  46. ),
  47. ),
  48. );
  49. }
  50. }

执行效果如下,点击灰色矩形,就可以看到整个动画效果:
Screenshot_1540881623