精华内容
下载资源
问答
  • Flutter 动画

    2019-11-28 22:59:00
    Flutter动画中用到的基本概念 Flutter动画中有4个比较重要的角色:Animation、Controller、Curve、Tween,先来了解一下这四个角色 1.1 Animation Animation是Flutter动画库中的核心类,用于插入指导动画的值 ...

    Flutter动画中用到的基本概念

    Flutter动画中有4个比较重要的角色:Animation、Controller、Curve、Tween,先来了解一下这四个角色

    1.1 Animation

    Animation是Flutter动画库中的核心类,用于插入指导动画的值

    Animation对象知道动画当前的状态(比如开始还是停止),可以使用addListener和addStatusListener监听动画状态改变。

       animation.addListener((){
          //调用setState来刷新界面
          setState(() {
          });
        });
        animation.addStatusListener((status){
          debugPrint('status $status');
          switch (status){
            //动画一开始就停止了
            case AnimationStatus.dismissed:
              break;
            //动画从头到尾都在播放
            case AnimationStatus.forward:
              break;
            //动画从结束到开始倒着播放
            case AnimationStatus.reverse:
              break;
            //动画播放完停止
            case AnimationStatus.completed:
              break;
          }
        });
    
    1. addListener: 每一帧都会调用,调用之后一般使用setState来刷新界面
    2. addStatusListener:监听动画当前的状态 如动画开始、结束、正向或反向

    在Flutter中,Animation对象本身和UI渲染没有任何关系。Animation是一个抽象类,它拥有其当前值和状态(完成或停止)。其中一个比较常用的Animation类是Animation<double>,还可以生成除double之外的其他类型值,如:Animation<Color>Animation<Size>

    1.2 AnimationController

    用来管理Animation,它继承自Animation,是个特殊的Animation,屏幕每刷新一帧,它都会生成一个新值,需要一个vsync参数,vsync的存在可以防止后台动画消耗不必要的资源

    vsync的值怎么获得,可以让stateful对象扩展使用TickerProviderStateMixin比如:

    class AnimationDemoHome extends StatefulWidget {
      @override
      _AnimationDemoHomeState createState() => _AnimationDemoHomeState();
    }
    
    class _AnimationDemoHomeState extends State<AnimationDemoHome> with TickerProviderStateMixin{...}
    

    AnimationController在默认情况下,在给定的时间段内,AnimationController会生成0.0到1.0的数字。

    它可以控制动画,比如使用.forward()方法可以启动一个动画,.stop()可以结束一个动画,.reverse()启动反向动画。

      AnimationController({
        double value,
        this.duration,
        this.reverseDuration,
        this.debugLabel,
        this.lowerBound = 0.0,
        this.upperBound = 1.0,
        this.animationBehavior = AnimationBehavior.normal,
        @required TickerProvider vsync,
      }) 
    

    看一下AnimationController的构造方法,有一个必须的参数TickerProvider,就是前面给定的TickerProviderStateMixin

    在StatefulWidget中创建一个AnimationController对象

      animationController = AnimationController(
    //      lowerBound: 32.0,
    //      upperBound: 100.0,
          duration: Duration(milliseconds: 2000),
          vsync: this
        );
    

    1.3 CurvedAnimation

    定义动画曲线,运动过程,比如匀速,先加速在减速等等

     CurvedAnimation({
        @required this.parent,
        @required this.curve,
        this.reverseCurve,
      })
    

    它有两个必要的参数parent和curve。parent就是前面的AnimationController对象,curve就是动画运行的曲线,相当于Android属性动画中的插值器curve都有哪些取值呢

    curve曲线 动画过程
    linear 匀速的
    decelerate 匀减速
    ease 先加速后减速
    easeIn 开始慢后面快
    easeOut 开始快后面慢
    easeInOut 先慢在快在慢

    上面是常用的一些曲线,还有很多中曲线运动的方式可以去curve.dart源码中去看,源码注释中有mp4的链接,可以清楚的看到动画运动的视频。

    abstract class Curve {
      const Curve();
    
      double transform(double t) {
        assert(t >= 0.0 && t <= 1.0);
        if (t == 0.0 || t == 1.0) {
          return t;
        }
        return transformInternal(t);
      }
    
      @protected
      double transformInternal(double t) {
        throw UnimplementedError();
      }
      ...
    }
    

    如果系统提供的运动曲线仍然无法满足我们的需求,那就可以继承Curve来自己实现一个。上面的代码可以看到Curve是一个抽象类,继承它并重写transform方法即可。比如我们可以自己在里面实现一个sin或者cos函数的曲线。例如

    class ShakeCurve extends Curve {
      @override
      double transform(double t) {
        return math.sin(t * math.PI * 2);
      }
    }
    

    创建一个CurvedAnimation对象

    CurvedAnimation curvedAnimation =
    CurvedAnimation(parent: animationController,curve: Curves.bounceOut);
    

    1.4 Tween:

    给动画对象插入一个范围值

    默认情况下,AnimationController对象的范围从0.0到1.0,如果我们想要更大的范围,就需要使用到Tween了。比如

    Tween tween = Tween(begin: 32.0,end: 100.0);
    

    class Tween<T extends dynamic> extends Animatable<T>Tween继承自Animatable,接收一个begin和一个end值,Tween的职责就是定义从输入范围到输出范围的映射。所以这两个值必须能进行加减乘的运算。

    要使用Tween对象,调用其animate()方法,传入一个控制器对象,返回一个Animation对象。例如,

    Animation   animation = Tween(begin: 32.0,end: 100.0).animate(curvedAnimation);
    Animation   animationColor = ColorTween(begin: Colors.red,end: Colors.green).animate(curvedAnimation);
    

    动画的使用

    2.1 Animation动画

    动画的四个角色都了解了,下面开始使用这些角色来构建一个动画,动画效果如下图

    在这里插入图片描述

    有一个心形的button,点击的时候放大并且颜色渐变,在点击的时候原路返回

    class AnimateDemo1 extends StatefulWidget {
      @override
      _AnimateDemo1State createState() => _AnimateDemo1State();
    }
    
    class _AnimateDemo1State extends State<AnimateDemo1> with SingleTickerProviderStateMixin{
      AnimationController animationController;
      Animation animationSize;
      Animation animationColor;
      CurvedAnimation curvedAnimation;
    
      //Tween sizeTween;
      //Tween colorTween;
      @override
      void initState() {
        super.initState();
        animationController = AnimationController(
            duration: Duration(milliseconds: 1000),
            vsync: this
        );
        //设置插值器  这里使用一个默认的插值器bounceInOut
        curvedAnimation = CurvedAnimation(parent: animationController,curve: Curves.bounceOut);
        animationSize = Tween(begin: 32.0,end: 100.0).animate(curvedAnimation);
        animationColor = ColorTween(begin: Colors.red,end: Colors.green).animate(curvedAnimation);
        animationController.addListener((){
          //刷新界面
          setState(() {});
        });
      }
    
      @override
      void dispose() {
        super.dispose();
        animationController.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Center(
          child: IconButton(
            icon: Icon(Icons.favorite),
            iconSize: animationSize.value,
            color: animationColor.value,
            //iconSize: sizeTween.evaluate(curvedAnimation),
            //color: colorTween.evaluate(curvedAnimation),
            onPressed: (){
              switch(animationController.status){
                case AnimationStatus.completed:
                  animationController.reverse();
                  break;
                default:
                  animationController.forward();
              }
            },
          ),
        );
      }
    }
    
    • 通过animation.value可以拿到动画当前的值,然后赋值给当前需要动画的控件的相关属性即可
    • 需要在addListener中调用setState来刷新界面,否则没效果
    • 需要注意 animationController需要在dispose()页面销毁的时候释动画资源。
    • 如果没有调用Tween的animate方法来构建一个Animation,可以在使用的地方使用如上面代码中sizeTween.evaluate(curvedAnimation)的方式来获取当前值。

    2.2使用AnimatedWidget

    2.1中每次写动画都需要在addListener中设置setState来更新UI,有点麻烦,系统给提供了一个AnimatedWidget,它内部封装了addListener和setState的逻辑,我们只需要传给它AnimationController和Animation就行了。

    而且我们可以自定义一个Widget继承它,让动画跟原来的视图代码分离

    class AnimationDemo2 extends StatefulWidget {
      @override
      _AnimationDemo2State createState() => _AnimationDemo2State();
    }
    
    class _AnimationDemo2State extends State<AnimationDemo2> with SingleTickerProviderStateMixin{
    
      AnimationController animationController;
      Animation animationSize;
      Animation animationColor;
      CurvedAnimation curvedAnimation;
      @override
      void initState() {
        super.initState();
        animationController = AnimationController(
          duration: Duration(milliseconds: 1000),
          vsync: this
        );
        //设置插值器  这里使用一个默认的插值器bounceInOut
        curvedAnimation = CurvedAnimation(parent: animationController,curve: Curves.bounceOut);
        animationSize = Tween(begin: 32.0,end: 100.0).animate(curvedAnimation);
        animationColor = ColorTween(begin: Colors.red,end: Colors.green).animate(curvedAnimation);
      }
    
      @override
      void dispose() {
        super.dispose();
        animationController.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Center(
          child: AnimationHeart(
             animations: [
               animationSize,animationColor
             ],
            controller: animationController,
          ),
        );
      }
    }
    //动画代码抽离
    class AnimationHeart extends AnimatedWidget{
      AnimationController controller;
      List animations;
      AnimationHeart({ this.animations,
        this.controller,}):super(listenable:controller);
    
      @override
      Widget build(BuildContext context) {
        return IconButton(
          icon: Icon(Icons.favorite),
          iconSize: animations[0].value,
          color: animations[1].value,
          onPressed: (){
            switch(controller.status){
              case AnimationStatus.completed:
                controller.reverse();
                break;
              default:
                controller.forward();
            }
          },
        );
      }
    }
    

    自定义一个AnimationHeart继承自AnimatedWidget,在构造方法中将AnimationController和Animation传过来。其余的跟2.1中一样,最终效果也一样。

    2.3使用AnimatedBuilder

    Flutter中还可以使用AnimatedBuilder来构建一个动画

    class AnimateDemo3 extends StatefulWidget {
      @override
      _AnimateDemo3State createState() => _AnimateDemo3State();
    }
    
    class _AnimateDemo3State extends State<AnimateDemo3> with SingleTickerProviderStateMixin{
    
      AnimationController animationController;
      Animation animationSize;
      Animation animationColor;
      CurvedAnimation curvedAnimation;
      @override
      void initState() {
        super.initState();
        animationController = AnimationController(
            duration: Duration(milliseconds: 1000),
            vsync: this
        );
        //设置插值器  这里使用一个默认的插值器bounceInOut
        curvedAnimation = CurvedAnimation(parent: animationController,curve: Curves.bounceOut);
        animationSize = Tween(begin: 32.0,end: 100.0).animate(curvedAnimation);
        animationColor = ColorTween(begin: Colors.red,end: Colors.green).animate(curvedAnimation);
      }
    
      @override
      void dispose() {
        super.dispose();
        animationController.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return AnimatedBuilder(
          animation: animationController,
          builder: (context,child){
            return Center(
              child: IconButton(
                icon: Icon(Icons.favorite),
                iconSize: animationSize.value,
                color: animationColor.value,
                onPressed: (){
                  switch(animationController.status){
                    case AnimationStatus.completed:
                      animationController.reverse();
                      break;
                    default:
                      animationController.forward();
                  }
                },
              ),
            );
          },
        );
      }
    }
    

    实例化四个动画元素的代码跟前面还是一样,主要是在build代码块中使用AnimatedBuilder构建,传入animation对象。看起来比2.2中的方式也没有简单多少,不过看一下它的构造方法,系统还给提供了一个可选的参数child,让它天然就支持封装。

    const AnimatedBuilder({
        Key key,
        @required Listenable animation,
        @required this.builder,
        this.child,
      })
    
    • 必需要一个Listenable,Animation就是Listenable
    • 必需要一个builder,前面的代码中知道builder中需要传一个context和一个child
    • 可以传一个child。传入的这个child最终会传入到builder中

    上面的例子中我们是直接在builder中创建了一个控件,既然child可以传进来,那么我们可以把一个类型的动画封装一下比如缩放动画,渐变动画等,以后只要把需要此动画的小部件传进来,这个小部件就有这个动画了。

    比如下面定义一个可以缩放的小部件。

    class ScaleAnimate extends StatelessWidget {
      final Animation animation;
      final Widget child;
      ScaleAnimate({@required this.animation,@required this.child});
      @override
      Widget build(BuildContext context) {
        return AnimatedBuilder(
          animation: animation,
          builder: (context,child){
            return SizedBox(
                width: animation.value,
                height: animation.value,
                child: child,
              );
          },
          child: child,
        );
      }
    }
    

    Hero动画

    Hero动画很简单不过在平时的项目中也经常用到,主要用在路由页面之间切换。比如一个头像点击看大图,或者新闻列表页面,点击看详情,这种共享式的无缝切换。

    动画效果如下图

    在这里插入图片描述

    class AnimateDemo4 extends StatefulWidget {
      @override
      _AnimateDemo4State createState() => _AnimateDemo4State();
    }
    
    class _AnimateDemo4State extends State<AnimateDemo4> {
      @override
      Widget build(BuildContext context) {
        return Center(
          child: InkWell(
            child: Hero(
              tag: "avator",
              child: ClipOval(
                child: Image.network('http://ww1.sinaimg.cn/large/0065oQSqly1fsfq1k9cb5j30sg0y7q61.jpg',width: 100,),
              ),
            ),
            onTap: (){
              Navigator.of(context).push(MaterialPageRoute(builder: (context){
                return Scaffold(
                  body: Center(
                    child: Hero(
                      tag: "avator",
                      child: Image.network('http://ww1.sinaimg.cn/large/0065oQSqly1fsfq1k9cb5j30sg0y7q61.jpg'),
                    ),
                  ),
                );
              }));
            },
          ),
        );
      }
    }
    
    • 当前页面的圆形小图和详情页面的大图都使用Hero包裹。
    • 必须使用相同的tag,Flutter Framework通过tag来确定他们之间的关系。

    交织动画

    有时候我们需要实现一组复杂的动画,比如在0.1-0.2秒缩放,从0.2-0.4秒颜色渐变,从0.4-0.8秒左右移动,这时候使用交织动画可以方便的完成,使用交织动画需要注意下面几点

    • 需要使用多个Animation对象
    • 一个AnimationController控制所有的动画对象
    • 给每一个动画对象指定时间间隔(Interval)

    在这里插入图片描述

    class AnimateDemo5 extends StatefulWidget {
      @override
      _AnimateDemo5State createState() => _AnimateDemo5State();
    }
    
    class _AnimateDemo5State extends State<AnimateDemo5> with TickerProviderStateMixin{
      AnimationController _controller;
      @override
      void initState() {
        super.initState();
        _controller = AnimationController(
            duration: const Duration(milliseconds: 2000),
            vsync: this
        );
      }
        @override
      void dispose() {
        super.dispose();
        _controller.dispose();
      }
      @override
      Widget build(BuildContext context) {
        return Center(
          child: Column(
            children: <Widget>[
              SizedBox(height: 30,),
              Center(
                child: StaggerAnimation(controller: _controller,),
              ),
              SizedBox(height: 30,),
              RaisedButton(
                child: Text("点击开始"),
                onPressed: () {
                  _play();
                },
                textColor: Theme.of(context).primaryColor,
                splashColor: Colors.grey[400],
              )
            ],
          ),
        );
      }
    
      void _play() async{
        //先正向执行动画
        await _controller.forward().orCancel;
        //再反向执行动画
        await _controller.reverse().orCancel;
      }
    }
    
    class StaggerAnimation extends StatelessWidget {
      final AnimationController controller;
      Animation<double> width,height;
      Animation<EdgeInsets> padding;
      Animation<Color> color;
      Animation<BorderRadius> borderRadius;
    
      StaggerAnimation({Key key,this.controller}): super(key:key){
        height = Tween<double>(
            begin: 0,
            end: 200)
            .animate(CurvedAnimation(parent: controller, curve: Interval(0.0,0.4,curve: Curves.ease)));
        width = Tween<double>(
            begin: 50,
            end: 200)
            .animate(CurvedAnimation(parent: controller, curve: Interval(0.0,0.4,curve: Curves.ease)));
        padding = Tween<EdgeInsets>(
          begin:EdgeInsets.only(left: .0),
          end:EdgeInsets.only(left: 100.0),
        ).animate(CurvedAnimation(parent: controller, curve: Interval(0.6, 1.0, curve: Curves.ease)),);
        color = ColorTween(
          begin:Colors.green ,
          end:Colors.red,
        ).animate(CurvedAnimation(parent: controller, curve: Interval(0.0, 0.4, curve: Curves.ease,)));
        borderRadius = BorderRadiusTween(
          begin: BorderRadius.circular(3),
          end: BorderRadius.circular(35),
        ).animate(CurvedAnimation(parent: controller, curve: Interval(0.4, 0.6,curve: Curves.ease,),));
      }
    
      @override
      Widget build(BuildContext context) {
        return AnimatedBuilder(
          animation: controller,
          builder: (context,child){
            return Container(
              alignment: Alignment.bottomCenter,
              padding:padding.value ,
              child: Container(
                width: width.value,
                height: height.value,
                decoration: BoxDecoration(
                    color: color.value,
                    border: Border.all(color: Colors.blue,width: 3),
                    borderRadius:borderRadius.value
                ),
              ),
            );
          },
        );
      }
    }
    
    • StaggerAnimation中定义了5个动画,宽,高,颜色,左边距,圆角
    • 使用Interval来定义某个动画执行的时机
    • 最后异步启动动画。
    展开全文
  • flutter动画

    2020-04-17 11:29:18
    flutter动画本质就是自动生成一个线性的值,生成的间隔 和时间 控制动画的效果, 官网动画直通车这里面有教你写几个简单的小栗子 可以使用Tween来配置动画以生成不同的范围或数据类型的值,比如: final Tween ...

    flutter线性动画

    flutter动画本质就是自动生成一个线性的值,生成的间隔 和时间 控制动画的效果,
    官网动画直通车这里面有教你写几个简单的小栗子
    可以使用Tween来配置动画以生成不同的范围或数据类型的值,比如:

    final Tween doubleTween = new Tween<double>(begin: -200.0, end: 0.0);
    

    Tween对象不存储任何状态。相反,它提供了evaluate(Animation animation)方法将映射函数应用于动画当前值。 Animation对象的当前值可以通过value()方法取到。evaluate函数还执行一些其它处理,例如分别确保在动画值为0.0和1.0时返回开始和结束状态。

    controller = AnimationController(
            duration: const Duration(milliseconds: 1000), vsync: this);
        curve = new CurvedAnimation(parent: controller, curve: Curves.easeInSine);
        animation = Tween<double>(begin: st, end: ed).animate(curve);
        animation.addListener((){
          setState(() {});
          print(animation.value);
        }) ;
    

    也就是说Tween的.animate方法,传给他动画控制器和线性会返回一个Animation对象。
    vsync: this动画的不在当前屏幕时,就不需要消耗不必要的资源
    curve可以自定义 也可以用官网写好的常用线性效果

    这有个点击旋转的例子,可以参考:

    在这里插入图片描述

    import 'dart:math' as math;
    import 'package:flutter/material.dart';
    import 'package:charts_flutter/flutter.dart' as charts;
    import 'package:flutter_animation/demo/animation.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Animation',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: MyHomePage(),
        );
      }
    }
    
    //AnimatedWidget(基类)中会自动调用addListener()和setState()。
    class AnimatedBox extends AnimatedWidget {
      Widget build(BuildContext context) {
        final Animation<double> animation = listenable;
        return Center(
          child: Text('s'),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class LinearSales {
      final int year;
      final int sales;
      LinearSales(this.year, this.sales);
    }
    
    class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
      static double st = 0.0;
      static double ed = 0.0; 
      double an = 0.0;
      DragDownDetails  point;
    
      // static final _tween = new Tween<double>(begin: st, end: ed);
      Animation doubleTween;
    
      AnimationController controller;
      Animation<double> animation;
      Animation curve;
    
      initState() {
        super.initState();
        controller = AnimationController(
            duration: const Duration(milliseconds: 1000), vsync: this);
        curve = new CurvedAnimation(parent: controller, curve: Curves.easeInSine);
        animation = Tween<double>(begin: st, end: ed).animate(curve);
        animation.addListener((){
          setState(() {});
        }) ;
      }
    
    //传度数返回角度
      angle(double angle) {
        return (math.pi / 180) * angle;
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            appBar: AppBar(
              title: Text('en'),
            ),
            body: GestureDetector(
              child: Transform.rotate(
                angle: angle(animation.value),
                child: Charts.create(),
              ),
              onPanUpdate: (details) {
                // double x = details.localPosition.dx;
                // double y = details.localPosition.dy;
                // setState(() {
                //   //两点坐标与中点的(180,350)夹角
                // an = math.atan2(x-180,350-y)/math.pi*180;
                // });
              },
              onPanDown: (details) {
                point = details;
                print(point);
                setState(() {
                  ed = ed + 45;
                  animation = Tween<double>(begin: st, end: ed).animate(curve);
                  controller.reset();
                  controller.forward();
                  st = ed;
                });
              },
            ));
      }
      dispose() {
        controller.dispose();
        super.dispose();
      }
    }
    
    class Charts extends StatelessWidget {
      final List<charts.Series> seriesList;
    
      Charts(this.seriesList);
    
      factory Charts.create() {
        return Charts(_createSampleData());
      }
    
      @override
      Widget build(BuildContext context) {
        // TODO: implement build
        return charts.PieChart(seriesList,
            animate: false,
            defaultRenderer: new charts.ArcRendererConfig(arcRendererDecorators: [
              new charts.ArcLabelDecorator(
                  labelPosition: charts.ArcLabelPosition.outside)
            ]));
      }
    
      static List<charts.Series<DataRow, String>> _createSampleData() {
        final data = [
          new DataRow("1", "A", 123),
          new DataRow("3", "B", 322),
          new DataRow("4", "C", 322),
          new DataRow("5", "D", 322)
        ];
        return [
          new charts.Series<DataRow, String>(
              id: "serId",
              // keyFn: (DataRow dr, _) => dr.id,
              domainFn: (DataRow dr, _) => dr.label,
              measureFn: (DataRow dr, _) => dr.value,
              data: data)
        ];
      }
    }
    
    class DataRow {
      final String id;
      final String label;
      final double value;
      DataRow(this.id, this.label, this.value);
    }
    
    展开全文
  • Flutter动画

    2020-07-30 16:42:25
    Flutter动画抽象 Flutter中也对动画进行了抽象,主要涉及Animation、Curve、Controller、Tween这四个角色,它们一起配合来完成一个完整动画,下面我们一一来介绍它们。 Animation Animation是一个抽象类,它...

    Flutter中动画抽象

    Flutter中也对动画进行了抽象,主要涉及Animation、Curve、Controller、Tween这四个角色,它们一起配合来完成一个完整动画,下面我们一一来介绍它们。

    Animation

    Animation是一个抽象类,它本身和UI渲染没有任何关系,而它主要的功能是保存动画的插值和状态;其中一个比较常用的Animation类是Animation<double>Animation对象是一个在一段时间内依次生成一个区间(Tween)之间值的类。Animation对象在整个动画执行过程中输出的值可以是线性的、曲线的、一个步进函数或者任何其他曲线函数等等,这由Curve来决定。 根据Animation对象的控制方式,动画可以正向运行(从起始状态开始,到终止状态结束),也可以反向运行,甚至可以在中间切换方向。Animation还可以生成除double之外的其他类型值,如:Animation<Color> 或Animation<Size>。在动画的每一帧中,我们可以通过Animation对象的value属性获取动画的当前状态值。

    动画通知

    我们可以通过Animation来监听动画每一帧以及执行状态的变化,Animation有如下两个方法:

    1. addListener();它可以用于给Animation添加帧监听器,在每一帧都会被调用。帧监听器中最常见的行为是改变状态后调用setState()来触发UI重建。
    2. addStatusListener();它可以给Animation添加“动画状态改变”监听器;动画开始、结束、正向或反向(见AnimationStatus定义)时会调用状态改变的监听器。

    读者在此只需要知道帧监听器和状态监听器的区别,在后面的章节中我们将会举例说明。

    Curve

    动画过程可以是匀速的、匀加速的或者先加速后减速等。Flutter中通过Curve(曲线)来描述动画过程,我们把匀速动画称为线性的(Curves.linear),而非匀速动画称为非线性的。

    我们可以通过CurvedAnimation来指定动画的曲线,如:

    final CurvedAnimation curve =
        new CurvedAnimation(parent: controller, curve: Curves.easeIn);
    

    CurvedAnimationAnimationController(下面介绍)都是Animation<double>类型。CurvedAnimation可以通过包装AnimationControllerCurve生成一个新的动画对象 ,我们正是通过这种方式来将动画和动画执行的曲线关联起来的。我们指定动画的曲线为Curves.easeIn,它表示动画开始时比较慢,结束时比较快。 Curves 类是一个预置的枚举类,定义了许多常用的曲线,下面列几种常用的:

    Curves曲线 动画过程
    linear 匀速的
    decelerate 匀减速
    ease 开始加速,后面减速
    easeIn 开始慢,后面快
    easeOut 开始快,后面慢
    easeInOut 开始慢,然后加速,最后再减速

    除了上面列举的, Curves 类中还定义了许多其它的曲线,在此便不一一介绍,读者可以自行查看Curves类定义。

    当然我们也可以创建自己Curve,例如我们定义一个正弦曲线:

    class ShakeCurve extends Curve {
      @override
      double transform(double t) {
        return math.sin(t * math.PI * 2);
      }
    }
    

    AnimationController

    AnimationController用于控制动画,它包含动画的启动forward()、停止stop() 、反向播放 reverse()等方法。AnimationController会在动画的每一帧,就会生成一个新的值。默认情况下,AnimationController在给定的时间段内线性的生成从0.0到1.0(默认区间)的数字。 例如,下面代码创建一个Animation对象(但不会启动动画):

    final AnimationController controller = new AnimationController(
        duration: const Duration(milliseconds: 2000), vsync: this);
    

    AnimationController生成数字的区间可以通过lowerBoundupperBound来指定,如:

    final AnimationController controller = new AnimationController( 
     duration: const Duration(milliseconds: 2000), 
     lowerBound: 10.0,
     upperBound: 20.0,
     vsync: this
    );
    

    AnimationController派生自Animation<double>,因此可以在需要Animation对象的任何地方使用。 但是,AnimationController具有控制动画的其他方法,例如forward()方法可以启动正向动画,reverse()可以启动反向动画。在动画开始执行后开始生成动画帧,屏幕每刷新一次就是一个动画帧,在动画的每一帧,会随着根据动画的曲线来生成当前的动画值(Animation.value),然后根据当前的动画值去构建UI,当所有动画帧依次触发时,动画值会依次改变,所以构建的UI也会依次变化,所以最终我们可以看到一个完成的动画。 另外在动画的每一帧,Animation对象会调用其帧监听器,等动画状态发生改变时(如动画结束)会调用状态改变监听器。

    duration表示动画执行的时长,通过它我们可以控制动画的速度。

    注意: 在某些情况下,动画值可能会超出AnimationController的[0.0,1.0]的范围,这取决于具体的曲线。例如,fling()函数可以根据我们手指滑动(甩出)的速度(velocity)、力量(force)等来模拟一个手指甩出动画,因此它的动画值可以在[0.0,1.0]范围之外 。也就是说,根据选择的曲线,CurvedAnimation的输出可以具有比输入更大的范围。例如,Curves.elasticIn等弹性曲线会生成大于或小于默认范围的值。

    Ticker

    当创建一个AnimationController时,需要传递一个vsync参数,它接收一个TickerProvider类型的对象,它的主要职责是创建Ticker,定义如下:

    abstract class TickerProvider {
      //通过一个回调创建一个Ticker
      Ticker createTicker(TickerCallback onTick);
    }
    

    Flutter应用在启动时都会绑定一个SchedulerBinding,通过SchedulerBinding可以给每一次屏幕刷新添加回调,而Ticker就是通过SchedulerBinding来添加屏幕刷新回调,这样一来,每次屏幕刷新都会调用TickerCallback。使用Ticker(而不是Timer)来驱动动画会防止屏幕外动画(动画的UI不在当前屏幕时,如锁屏时)消耗不必要的资源,因为Flutter中屏幕刷新时会通知到绑定的SchedulerBinding,而Ticker是受SchedulerBinding驱动的,由于锁屏后屏幕会停止刷新,所以Ticker就不会再触发。

    通常我们会将SingleTickerProviderStateMixin添加到State的定义中,然后将State对象作为vsync的值,这在后面的例子中可以见到。

    Tween

    默认情况下,AnimationController对象值的范围是[0.0,1.0]。如果我们需要构建UI的动画值在不同的范围或不同的数据类型,则可以使用Tween来添加映射以生成不同的范围或数据类型的值。例如,像下面示例,Tween生成[-200.0,0.0]的值:

    final Tween doubleTween = new Tween<double>(begin: -200.0, end: 0.0);
    

    Tween构造函数需要beginend两个参数。Tween的唯一职责就是定义从输入范围到输出范围的映射。输入范围通常为[0.0,1.0],但这不是必须的,我们可以自定义需要的范围。

    Tween继承自Animatable<T>,而不是继承自Animation<T>Animatable中主要定义动画值的映射规则。

    下面我们看一个ColorTween将动画输入范围映射为两种颜色值之间过渡输出的例子:

    final Tween colorTween =
        new ColorTween(begin: Colors.transparent, end: Colors.black54);
    

    Tween对象不存储任何状态,相反,它提供了evaluate(Animation<double> animation)方法,它可以获取动画当前映射值。 Animation对象的当前值可以通过value()方法取到。evaluate函数还执行一些其它处理,例如分别确保在动画值为0.0和1.0时返回开始和结束状态。

    Tween.animate

    要使用Tween对象,需要调用其animate()方法,然后传入一个控制器对象。例如,以下代码在500毫秒内生成从0到255的整数值。

    final AnimationController controller = new AnimationController(
        duration: const Duration(milliseconds: 500), vsync: this);
    Animation<int> alpha = new IntTween(begin: 0, end: 255).animate(controller);
    

    注意animate()返回的是一个Animation,而不是一个Animatable

    以下示例构建了一个控制器、一条曲线和一个Tween:

    final AnimationController controller = new AnimationController(
        duration: const Duration(milliseconds: 500), vsync: this);
    final Animation curve =
        new CurvedAnimation(parent: controller, curve: Curves.easeOut);
    Animation<int> alpha = new IntTween(begin: 0, end: 255).animate(curve);

    参考文章:https://book.flutterchina.club/chapter9/intro.html
    Flutter-动画(平移/缩放/旋转/Alpha)https://www.jianshu.com/p/ee243d5d0a41 

    展开全文
  • flutter 动画

    2021-03-23 20:31:43
    https://flutter.cn/docs/cookbook/animation/physics-simulation 为页面切换加入动画效果PageRouteBuilder 提供了一个 Animation 对象。这个 Animation 能够通过结合 Tween 以及 Curve 对象来

    主要有四种用法:

    AnimatedContainer 在使用新属性进行重建时,将会自动在旧值和新值之间生成动画。

    AnimatedOpacity使用不透明动画让元素淡入淡出

    Widget 的物理模拟动画效果

    https://flutter.cn/docs/cookbook/animation/physics-simulation

    为页面切换加入动画效果PageRouteBuilder 提供了一个 Animation 对象。这个 Animation 能够通过结合 Tween 以及 Curve 对象来自定义路由转换动画。

    展开全文
  • 一、Flutter 动画类型、 二、Flutter 动画的核心类、 三、相关资源
  • Flutter动画explorer

    2019-08-10 05:08:18
    Flutter动画explorer
  • Flutter】Animation 动画 ( Flutter 动画基本流程 | 创建动画控制器 | 创建动画 | 设置值监听器 | 设置状态监听器 | 布局中使用动画值 | 动画运行 ) | ... 博客源码快照
  • JavaScript开发Flutter动画explorer
  • Flutter 动画系列》组合动画

    千次阅读 2020-04-05 17:15:08
    老孟导读:在前面的文章中介绍了 《Flutter 动画系列》25种动画组件...《Flutter 动画系列》Google工程师带你选择Flutter动画控件: http://laomengit.com/flutter/module/animated_choose/ 在项目中动画效果很多...
  • 41Flutter动画

    2021-05-10 14:35:43
    Flutter动画 1.Flutter平移动画 动画属性: dismissed:动画初始状态 forward:动画从头到尾播放状态 reverse:动画从尾到头播放状态 completed:动画完成状态 值监听:addListener、removeListener 状态监听:...
  • Flutter 动画学习

    2021-04-07 09:50:40
    Flutter中,隐式动画(Implicit Animation)是一种自动播放的动画效果,只要Widget发生改变,就会自动播放一个过渡动画。大部分以Animated开头的Widget都是带有隐式动画效果的控件。 只在自身属性产生变化时产生...
  • Flutter 动画入门

    2020-04-21 21:59:57
    Flutter动画核心类:Animation类,它可以判断当前动画的状态(开始,停止,移动,前进,反向),它是由AnimationController管理的,并通过Listeners和StatusListeners管理动画状态的所发生的变化,我们先对动画有了...
  • Flutter 动画体系

    2020-11-25 15:41:41
    Flutter动画体系 基础概念 Flutter中也对动画进行了抽象,主要涉及Animation、Curve、Controller、Tween这四个角色,它们一起配合来完成一个完整动画 Animation 它主要的功能是保存动画的插值和状态,根据...
  • Flutter 动画之TweenAnimationBuilder
  • JavaScript开发-Flutter动画explorer
  • flutter动画步骤

    2021-03-18 10:49:00
    flutter动画步骤 import 'package:flutter/material.dart'; import 'dart:math'; class MallContent extends StatefulWidget { @override _HomeContentState createState() => _HomeContentState(); } // 这里...
  • Flutter 动画基础

    2019-11-09 18:26:43
    Flutter中的动画系统基于Animation对象的,widget可以在build...flutter动画实现基于AnimationController对象。所以要先定义: AnimationController controller =new AnimationController(duration: const Duration...
  • Flutter 动画(一)

    2021-03-25 15:44:40
    Flutter 动画 文章目录Flutter 动画准备AnimationControllerTicker providers生命周期TweenSampleFlutterLogo从30-50的放大动画CurvedAnimationInterval 准备 void main() { runApp(MyWidget()); } class MyWidget...
  • Flutter动画(全)

    千次阅读 2019-09-05 14:14:58
    Flutter动画 1.Flutter Api自带的动画部件。 2.自定义动画。 3.结合自定义手势和自定义绘制来搞出各种炫酷的需求。 一 Flutter Api提供了用户足够操作的动画部件。我们可以通过这些部件来达到几乎所有的动画效果...
  • flutter动画效果| 自定义小部件| 自定义renderobject
  • Flutter动画基础教程

    千人学习 2019-08-25 19:40:24
    Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的用户界面。本课程以理论+实例的方式带领大家学习动画的基础理论及各种动画特效的编写。 本次课程包含以下内容: 动画基本概念 动画的本质 帧与FPS ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,271
精华内容 908
关键字:

flutter动画