美文网首页
Flutter 学习笔记 17 - 交错动画

Flutter 学习笔记 17 - 交错动画

作者: 三流之路 | 来源:发表于2019-05-21 19:30 被阅读0次

    多个动画组合在一起,可能有重叠。每个动画对应一个 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

    相关文章

      网友评论

          本文标题:Flutter 学习笔记 17 - 交错动画

          本文链接:https://www.haomeiwen.com/subject/extrjqtx.html