flutter-动画

作者: 逆光_初见 | 来源:发表于2021-03-08 16:45 被阅读0次

    动画与打包

    [TOC]

    动画

        Flutter中的动画系统基于`Animation`对象的,和之前的手势不同,它不是一个Widget,这是因为`Animation`对象本身和UI渲染没有任何关系。Animation是一个抽象类,就相当于一个定时器,它用于保存动画的插值和状态,并执行数值的变化。widget可以在`build`函数中读取`Animation`对象的当前值, 并且可以监听动画的状态改变。
    

    AnimationController

        AnimationController用于控制动画,它包含动画的启动`forward()`、停止`stop()` 、反向播放 `reverse()`等方法。AnimationController会在动画的每一帧,就会生成一个新的值。默认情况下,AnimationController在给定的时间段内线性的生成从0.0到1.0(默认区间)的数字。
    
    AnimationController controller = AnimationController( 
     duration: const Duration(milliseconds: 2000), //动画时间
     lowerBound: 10.0,  //生成数字的区间 
     upperBound: 20.0,  //10.0 - 20.0
     vsync: this  //TickerProvider 动画驱动器提供者
    );
    

    Ticker

    Ticker的作用是添加屏幕刷新回调,每次屏幕刷新都会调用`TickerCallback`。使用Ticker来驱动动画会防止屏幕外动画(动画的UI不在当前屏幕时,如锁屏时)消耗不必要的资源。因为Flutter中屏幕刷新时会通知Ticker,锁屏后屏幕会停止刷新,所以Ticker就不会再触发。最简单的做法为将`SingleTickerProviderStateMixin`添加到State的定义中。
    
    import 'package:flutter/material.dart';
    
    void main() => runApp(AnimationApp());
    
    class AnimationApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: "animation",
          home: Scaffold(
            appBar: AppBar(
              title: Text('animation'),
            ),
            body: AnimWidget(),
          ),
        );
      }
    }
    
    // 动画是有状态的
    class AnimWidget extends StatefulWidget {
      @override
      State<StatefulWidget> createState() {
        return _AnimWidgetState();
      }
    }
    
    class _AnimWidgetState extends State<AnimWidget>
        with SingleTickerProviderStateMixin {
      AnimationController controller;
      bool forward = true;
    
      @override
      void initState() {
        super.initState();
        controller = AnimationController(
          // 动画的时长
          duration: Duration(milliseconds: 2000),
          lowerBound: 10.0,
          upperBound: 100.0,
          // 提供 vsync 最简单的方式,就是直接混入 SingleTickerProviderStateMixin
          // 如果有多个AnimationController,则使用TickerProviderStateMixin。
          vsync: this,
        );
        //状态修改监听
        controller
          ..addStatusListener((AnimationStatus status) {
            debugPrint("状态:$status");
          })
          ..addListener(() {
            setState(() => {});
          });
    
        debugPrint("controller.value:${controller.value}");
      }
    
      @override
      Widget build(BuildContext context) {
        return Column(
          children: <Widget>[
            Container(
              width: controller.value,
              height: controller.value,
              color: Colors.blue,
            ),
            RaisedButton(
              child: Text("播放"),
              onPressed: () {
                if (forward) {
                  controller.forward();
                } else {
                  controller.reverse();
                }
                forward = !forward;
              },
            ),
            RaisedButton(
              child: Text("停止"),
              onPressed: () {
                controller.stop();
              },
            )
          ],
        );
      }
    }
    
    

    动画状态监听:在forword结束之后状态为completed。在reverse结束之后状态为dismissed

    Tween

    默认情况下,`AnimationController`对象值为:double类型,范围是0.0到1.0 。如果我们需要不同的范围或不同的数据类型,则可以使用Tween来配置动画以生成不同的范围或数据类型的值。要使用Tween对象,需要调用其`animate()`方法,然后传入一个控制器对象,同时动画过程中产生的数值由`Tween`的`lerp`方法决定。
    
    import 'package:flutter/material.dart';
    
    void main() => runApp(AnimationApp());
    
    class AnimationApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: "animation",
          home: Scaffold(
            appBar: AppBar(
              title: Text('animation'),
            ),
            body: AnimWidget(),
          ),
        );
      }
    }
    
    // 动画是有状态的
    class AnimWidget extends StatefulWidget {
      @override
      State<StatefulWidget> createState() {
        return _AnimWidgetState();
      }
    }
    
    class _AnimWidgetState extends State<AnimWidget>
        with SingleTickerProviderStateMixin {
      AnimationController controller;
    
      bool forward = true;
    
      Tween<Color> tween;
    
      @override
      void initState() {
        super.initState();
    
        controller = AnimationController(
          // 动画的时长
          duration: Duration(milliseconds: 2000),
          // 提供 vsync 最简单的方式,就是直接继承 SingleTickerProviderStateMixin
          vsync: this,
        );
        //使用Color
        tween = ColorTween(begin: Colors.blue, end: Colors.yellow);
        //添加动画值修改监听
        tween.animate(controller)..addListener(() => setState(() {}));
      }
    
      @override
      Widget build(BuildContext context) {
        return Column(
          children: <Widget>[
            Container(
              width: 100,
              height: 100,
              //获取动画当前值
              color: tween.evaluate(controller),
            ),
            RaisedButton(
              child: Text("播放"),
              onPressed: () {
                if (forward) {
                  controller.forward();
                } else {
                  controller.reverse();
                }
                forward = !forward;
              },
            ),
            RaisedButton(
              child: Text("停止"),
              onPressed: () {
                controller.stop();
              },
            )
          ],
        );
      }
    }
    
    

    Curve

    动画过程默认是线性的(匀速),如果需要非线形的,比如:加速的或者先加速后减速等。Flutter中可以通过Curve(曲线)来描述动画过程。
    
    import 'package:flutter/material.dart';
    
    void main() => runApp(AnimationApp());
    
    class AnimationApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: "animation",
          home: Scaffold(
            appBar: AppBar(
              title: Text('animation'),
            ),
            body: AnimWidget(),
          ),
        );
      }
    }
    
    // 动画是有状态的
    class AnimWidget extends StatefulWidget {
      @override
      State<StatefulWidget> createState() {
        return _AnimWidgetState();
      }
    }
    
    class _AnimWidgetState extends State<AnimWidget>
        with SingleTickerProviderStateMixin {
      AnimationController controller;
      Animation<double> animation;
      bool forward = true;
    
      @override
      void initState() {
        super.initState();
    
        controller = AnimationController(
          // 动画的时长
          duration: Duration(milliseconds: 2000),
          // 提供 vsync 最简单的方式,就是直接继承 SingleTickerProviderStateMixin
          vsync: this,
        );
    
        //弹性
        animation = CurvedAnimation(parent: controller, curve: Curves.bounceIn);
        //使用Color
        animation = Tween(begin: 10.0, end: 100.0).animate(animation)
          ..addListener(() {
            setState(() => {});
          });
      }
    
      @override
      Widget build(BuildContext context) {
        return Column(
          children: <Widget>[
            Container(
              //不需要转换
              width: animation.value,
              height: animation.value,
              //获取动画当前值
              color: Colors.blue,
            ),
            RaisedButton(
              child: Text("播放"),
              onPressed: () {
                if (forward) {
                  controller.forward();
                } else {
                  controller.reverse();
                }
                forward = !forward;
              },
            ),
            RaisedButton(
              child: Text("停止"),
              onPressed: () {
                controller.stop();
              },
            )
          ],
        );
      }
    }
    
    

    AnimatedWidget

        通过上面的学习我们能够感受到`Animation`对象本身和UI渲染没有任何关系。而通过`addListener()`和`setState()` 来更新UI这一步其实是通用的,如果每个动画中都加这么一句是比较繁琐的。AnimatedWidget类封装了调用`setState()`的细节,简单来说就是自动调用`setState()`。
    
    Flutter中已经封装了很多动画,比如对widget进行缩放,可以直接使用`ScaleTransition`
    
    import 'package:flutter/material.dart';
    
    void main() => runApp(AnimationApp());
    
    class AnimationApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: "animation",
          home: Scaffold(
            appBar: AppBar(
              title: Text('animation'),
            ),
            body: AnimWidget(),
          ),
        );
      }
    }
    
    // 动画是有状态的
    class AnimWidget extends StatefulWidget {
      @override
      State<StatefulWidget> createState() {
        return _AnimWidgetState();
      }
    }
    
    class _AnimWidgetState extends State<AnimWidget>
        with SingleTickerProviderStateMixin {
      AnimationController controller;
      Animation<double> animation;
      bool forward = true;
    
      @override
      void initState() {
        super.initState();
    
        controller = AnimationController(
          // 动画的时长
          duration: Duration(milliseconds: 2000),
          // 提供 vsync 最简单的方式,就是直接继承 SingleTickerProviderStateMixin
          vsync: this,
        );
    
        //弹性
        animation = CurvedAnimation(parent: controller, curve: Curves.bounceIn);
        //使用Color
        animation = Tween(begin: 10.0, end: 100.0).animate(animation);
      }
    
      @override
      Widget build(BuildContext context) {
        return Column(
          children: <Widget>[
            ScaleTransition(
              child:  Container(
                width: 100,
                height: 100,
                color: Colors.blue,
              ),
              scale: controller,
            ),
            RaisedButton(
              child: Text("播放"),
              onPressed: () {
                if (forward) {
                  controller.forward();
                } else {
                  controller.reverse();
                }
                forward = !forward;
              },
            ),
            RaisedButton(
              child: Text("停止"),
              onPressed: () {
                controller.stop();
              },
            )
          ],
        );
      }
    }
    
    

    Hero动画

    Hero动画就是在路由切换时,有一个共享的Widget可以在新旧路由间切换,由于共享的Widget在新旧路由页面上的位置、外观可能有所差异,所以在路由切换时会逐渐过渡,这样就会产生一个Hero动画。
    
    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          title: 'Flutter Demo',
          home: Scaffold(
              appBar: AppBar(
                title: Text("主页"),
              ),
              body: Route1()),
        );
      }
    }
    
    // 路由A
    class Route1 extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Container(
          alignment: Alignment.topCenter,
          child: InkWell(
            child: Hero(
              tag: "avatar", //唯一标记,前后两个路由页Hero的tag必须相同
              child: CircleAvatar(
                backgroundImage: AssetImage(
                  "assets/banner.jpeg",
                ),
              ),
            ),
            onTap: () {
              Navigator.push(context, MaterialPageRoute(builder: (_) {
                return Route2();
              }));
            },
          ),
        );
      }
    }
    
    class Route2 extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Center(
          child: Hero(
              tag: "avatar", //唯一标记,前后两个路由页Hero的tag必须相同
              child: Image.asset("assets/banner.jpeg")),
        );
      }
    }
    
    

    组合动画

    有些时候我们可能会需要执行一个动画序列执行一些复杂的动画。

    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          title: 'Flutter Demo',
          home: Route(),
        );
      }
    }
    
    class Route extends StatefulWidget {
      @override
      State<StatefulWidget> createState() {
        return RouteState();
      }
    }
    
    class RouteState extends State<Route> with SingleTickerProviderStateMixin {
      Animation<Color> color;
      Animation<double> width;
      AnimationController controller;
    
      @override
      void initState() {
        super.initState();
        controller = AnimationController(
          // 动画的时长
          duration: Duration(milliseconds: 2000),
          // 提供 vsync 最简单的方式,就是直接继承 SingleTickerProviderStateMixin
          vsync: this,
        );
    
        //高度动画
        width = Tween<double>(
          begin: 100.0,
          end: 300.0,
        ).animate(
          CurvedAnimation(
            parent: controller,
            curve: Interval(
              //间隔,前60%的动画时间 1200ms执行高度变化
              0.0, 0.6,
            ),
          ),
        );
    
        color = ColorTween(
          begin: Colors.green,
          end: Colors.red,
        ).animate(
          CurvedAnimation(
            parent: controller,
            curve: Interval(
              0.6, 1.0, //高度变化完成后 800ms 执行颜色编码
            ),
          ),
        );
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("主页"),
          ),
          body: InkWell(
            ///1、不用显式的去添加帧监听器,再调用setState()
            ///2、缩小动画构建的范围,如果没有builder,setState()将会在父widget上下文调用,导致父widget的build方法重新调用,现在只会导致动画widget的build重新调用
            child: AnimatedBuilder(
                animation: controller,
                builder: (context, child) {
                  return Container(
                    color: color.value,
                    width: width.value,
                    height: 100.0,
                  );
                }),
            onTap: () {
              controller.forward().whenCompleteOrCancel(() => controller.reverse());
            },
          ),
        );
      }
    }
    
    

    相关文章

      网友评论

        本文标题:flutter-动画

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