Flutter 学习笔记 17 - 交错动画

时间:2022-06-21
本文章向大家介绍Flutter 学习笔记 17 - 交错动画,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

多个动画组合在一起,可能有重叠。每个动画对应一个 Tween 对象,一个 AnimationController 控制所有动画。AnimationController 的值必须在 0.0 到 1.0 之间。

先看一个简单的例子:

class AnimatedLogo extends AnimatedWidget {
  // 两个 Animation
  static final _opacityTween = new Tween<double>(begin: 0.1, end: 1.0);
  static final _sizeTween = new Tween<double>(begin: 0.0, end: 300.0);
  
  AnimatedLogo({Key key, Animation<double> animation})
      : super(key: key, listenable: animation);

  Widget build(BuildContext context) {
    final Animation<double> animation = listenable;
    return Center(
      child: new Opacity(
        // 透明度动画
        opacity: _opacityTween.evaluate(animation),
        child: new Container(
          margin: new EdgeInsets.symmetric(vertical: 10.0),
          // 尺寸动画
          height: _sizeTween.evaluate(animation), 
          width: _sizeTween.evaluate(animation),
          child: new FlutterLogo(),
        ),
      ),    
    );
  }
}
//...

class AnimState extends State<AnimScreen> with SingleTickerProviderStateMixin {

  Animation<double> animation;
  AnimationController controller;

  @override
  initState() {
    super.initState();
    controller = new AnimationController(
        duration: const Duration(milliseconds: 2000), vsync: this);
    animation = new CurvedAnimation(parent: controller, curve: Curves.easeIn);
    controller.forward();
  }
  
  Widget build(BuildContext context) {
    return AnimatedLogo(animation: animation);
  }
  
  // ...
}

对于原来 Animation

animation = Tween(begin: 0.0, end: 300.0).animate(controller)

看 animate 方法的定义

Animation<T> animate(Animation<double> parent) {
  return _AnimatedEvaluation<T>(parent, this);
}

而以后调用 .value 时,看源码

@override
T get value => _evaluatable.evaluate(parent);

所以 _sizeTween.evaluate(animation) 这种用法和原来是等价的,只是原来先通过 animation() 方法返回了 Animation,然后直接调用这个 Animation 的 value。现在是直接在 Tween 上通过 evaluate 方法把外界的 Animation 传进去。


下面是个更复杂的例子,在 0.0 到 1.0 间连续做六个动画

15481272243303.png

六个动画创建六个 Tween,创建一个类管理这六个动画,在构造时创建对象

class StaggerAnimation extends StatelessWidget {
  final Animation<double> controller;
  final Animation<double> opacity;
  final Animation<double> width;
  final Animation<double> height;
  final Animation<EdgeInsets> padding;
  final Animation<BorderRadius> borderRadius;
  final Animation<Color> color;
  
  StaggerAnimation({ Key key, this.controller }) :
        opacity = Tween<double>(
          begin: 0.0,
          end: 1.0,
        ).animate(
          CurvedAnimation(
            parent: controller,
            curve: Interval(
              0.0, 0.100,
              curve: Curves.ease,
            ),
          ),
        ),

        width = Tween<double>(
          begin: 50.0,
          end: 150.0,
        ).animate(
          CurvedAnimation(
            parent: controller,
            curve: Interval(
              0.125, 0.250,
              curve: Curves.ease,
            ),
          ),
        ),

        height = Tween<double>(
            begin: 50.0,
            end: 150.0
        ).animate(
          CurvedAnimation(
            parent: controller,
            curve: Interval(
              0.250, 0.375,
              curve: Curves.ease,
            ),
          ),
        ),

        padding = EdgeInsetsTween(
          begin: const EdgeInsets.only(bottom: 16.0),
          end: const EdgeInsets.only(bottom: 75.0),
        ).animate(
          CurvedAnimation(
            parent: controller,
            curve: Interval(
              0.250, 0.375,
              curve: Curves.ease,
            ),
          ),
        ),

        borderRadius = BorderRadiusTween(
          begin: BorderRadius.circular(4.0),
          end: BorderRadius.circular(75.0),
        ).animate(
          CurvedAnimation(
            parent: controller,
            curve: Interval(
              0.375, 0.500,
              curve: Curves.ease,
            ),
          ),
        ),

        color = ColorTween(
          begin: Colors.indigo[100],
          end: Colors.orange[400],
        ).animate(
          CurvedAnimation(
            parent: controller,
            curve: Interval(
              0.500, 0.750,
              curve: Curves.ease,
            ),
          ),
        ),

        super(key: key);
}

然后重写 build 方法

final Animation<double> controller;

Widget _buildAnimation(BuildContext context, Widget child) {
  return Container(
    padding: padding.value, // 内边距动画
    alignment: Alignment.bottomCenter,
    child: Opacity(
      opacity: opacity.value, // 透明度动画
      child: Container(
        width: width.value, // 宽度动画
        height: height.value, // 高度动画
        decoration: BoxDecoration(
          color: color.value, // 颜色动画
          border: Border.all(
            color: Colors.indigo[300],
            width: 3.0,
          ),
          borderRadius: borderRadius.value, // 圆角动画
        ),
      ),
    ),
  );
}

@override
Widget build(BuildContext context) {
  return AnimatedBuilder(
    builder: _buildAnimation, // 动画变化时调用这个函数
    animation: controller, // 要执行的动画
  );
}

然后构建要做动画的 Widget

class StaggerDemo extends StatefulWidget {
  @override
  _StaggerDemoState createState() => _StaggerDemoState();
}

class _StaggerDemoState extends State<StaggerDemo> with TickerProviderStateMixin {
  AnimationController _controller;

  @override
  void initState() {
    super.initState();

    // 构建一个 Controller
    _controller = AnimationController(
        duration: const Duration(milliseconds: 2000),
        vsync: this
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  Future<void> _playAnimation() async {
    try {
      // 执行一遍再逆向执行一遍
      await _controller.forward().orCancel;
      await _controller.reverse().orCancel;
    } on TickerCanceled {
    }
  }

  @override
  Widget build(BuildContext context) {
    timeDilation = 10.0;
    return Scaffold(
      appBar: AppBar(
        title: const Text('Staggered Animation'),
      ),
      body: GestureDetector(
        behavior: HitTestBehavior.opaque,
        onTap: () {
          _playAnimation();
        },
        child: Center(
          child: Container(
            width: 300.0,
            height: 300.0,
            decoration: BoxDecoration(
              color: Colors.black.withOpacity(0.1),
              border: Border.all(
                color:  Colors.black.withOpacity(0.5),
              ),
            ),
            child: StaggerAnimation(
                controller: _controller.view
            ),
          ),
        ),
      ),
    );
  }
}

AnimationController 是 2000ms,而最后的 0.25 没有执行某个动画,逆序最开始也有 0.25,所以中间会有 2000*0.5=1000ms 的时间没有动画效果(Gif 图有点太快了)。

2019-01-22 15_48_53.gif