Flutter(十四)——动画的原理以及Tween与Curve的使用

简介: Flutter(十四)——动画的原理以及Tween与Curve的使用

前言


不管是Android还是IOS,我们都能看到用户在使用动画,交互动画可以作为用户的操作向导,不让操作乏味无趣,可以说没有动画的APP,是没有灵魂的App。本篇将讲解在Flutter中如何使用。



动画的原理


提到动画原理,就不得不说“动画帧”。比如手机拍摄的视频,虽然看起来展现的是一个连续播放的视频,但其实视频本身都是由一幅幅静态的图片组成,静态图片的连续展示就是一段视频。


这里由一个概念,即画面每秒传输帧数,经常玩游戏的应该非常清楚就是FPS帧率,FPS越高,游戏越流畅,同样,动画也是如此。目前大多数设备的刷新频率为60Hz,所以在通常情况下FPS为60fps时效果最佳,也就是每秒消耗的时间为16.67ms。在Flutter开发中,FPS已经达到了60fps,所以动画的流畅性也已达到原生动画的效果。


当然,在Flutter开发中,动画的核心类也是Animation类,它可以判断当前的动画状态(比如开始,停止,移动,前进,反向),但是它不关心屏幕上显示的任何组件。Animation是由AnimationController管理的,并通过Listeners和StateListeners管理动画状态所发生的变化。在Flutter中,通过Animation,AnimationController,Tween,Curve对动画进行了抽象(模型化)。


Animation


在动画中,使用最多的类型是Animation。Animation对象本身对手机屏幕是无感知的,而且,它对调用的build方法也是无感知的。它是一个抽象类,仅仅知道当前的动画插值和状态(complete,dismissed)。Animation对象是一个在一段时间内,依次生成一个区间值的类,其输出值是线性的,非线性的,也可以是一个步进函数或者其他曲线函数。控制器可以决定Animation动画的运行方式:正向,反向以及在中间进行切换.Animation也可以生成除double之外的其他值,比如Animation或Animation。


Animatable


Animatable是控制动画类型的类。比如在平移动画中,我们关系的是(x,y)的值,那么这个时候就需要Animatable控制(x,y)的值的变化。在颜色动画中,我们关心的是色值的变化,那么就需要Animatable控制色值的变化。在贝塞尔曲线运动中,我们关心的是路径是否按照贝塞尔方程式来生成(x,y),所以Animatable就要按照贝塞尔方程式的方式来改变(x,y)。


AnimationController


AnimationController即动画控制器,它负责在给定的时间段内,以线性的方式生成默认区间为(0.0,1.0)的数字。我们可以通过AnimationController来创建Animation对象,例如:

final AnimationController controller=AnimationController(duration:const Duration(seconds:2),vsync:this);

以上AnimationController的创建派生自Animation,它能告诉Flutter动画的控制器已经创建好对象了,并且处于准备状态。要真正让动画运作起来,则需要通过AnimationController的forward()方法来启动动画。其中,数值的产生取决与屏幕的刷新情况,一般每秒60帧。数值生成以后,每个Animation对象都会通过Listener进行回调。


Tween


Tween是补间动画,我们上面已经讲过AnimationController的取值范围是(0.0,1.0),如果我们需要不同范围或类型的值,就可以使用Tween,比如:

Tween doubleTween=Tween<double>(begin:0.0,end:300.0);

可以看到Tween构造函数有两个参数begin,end。而它的作用就是定义从输入方位到输出范围的映射。


Tween继承自Animatable,而不是Animation。虽然Animatable与Animation相似,但它不是必须输出double值,也可以输出ColorTween颜色值。


虽然Tween不会存储任何状态信息,但它给我们提供了evaluate(Animation animation)方法,并可以通过映射获取动画当前值。Animation当前值可以通过value方法来获取。evaluate还能执行其他处理,比如确保动画值分别为0.0和1.0时,返回开始和结束状态。若要使用Tween对象,需要调用animate方法,并传入一个控制器对象。


Tween实现循环放大与缩小


通过上面的理论学习,我们基本掌握了Animation的使用方法,下面我们直接实现一个动画来:

class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin{
  Animation<double> animation;
  AnimationController controller;
  initState(){
    super.initState();
    controller=AnimationController(duration: const Duration(milliseconds: 2000),vsync: this);
    animation=Tween(begin: 0.0,end: 300.0).animate(controller)..addStatusListener((status){
      if(status==AnimationStatus.completed){//动画在结束时停止的状态
        controller.reverse();//颠倒
      }else if(status==AnimationStatus.dismissed){//动画在开始时就停止的状态
        controller.forward();//向前
      }
    })..addListener((){
      setState(() {
      });
    });
    controller.forward();
  }
  @override
  void dispose() {
    controller.dispose();
    // TODO: implement dispose
    super.dispose();
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Container(
        height: animation.value,
        margin: EdgeInsets.symmetric(vertical: 10.0),
        width: animation.value,
        child: FlutterLogo(),
      ),
    );
  }
}


代码很简单,我们先定义了控制器与animation动画,然后实例化控制器,这里我们混入了SingleTickerProviderStateMixin类,因为初始化animationController的时候需要一个TickerProvider类型的参数Vsync,所以我们混入了TickerProvider的子类SingleTickerProviderStateMixin。


接着我们通过控制器初始化一个 Animation 对象,设置它的开始为0,结束为300,因为这里必须返回一个animation对象我们才能调用监听函数,所以我们这里用了级联操作符"…",如果不用级联操作符,也可以这样写:

animation=Tween(begin: 0.0,end: 300.0).animate(controller)
animation.addStatusListener((status){
      if(status==AnimationStatus.completed){//动画在结束时停止的状态
        controller.reverse();//颠倒
      }else if(status==AnimationStatus.dismissed){//动画在开始时就停止的状态
        controller.forward();//向前
      }
    }).addListener((){
      setState(() {
        print(animation.value);
      });
    });


接着我们通过AnimationStatus.completed判断动画是否为结束状态, 如果是结束状态,我们让它reverse反向执行动画,同样,如果是AnimationStatus.dismissed动画在开始时停止,我们就forward正常执行动画,这样就能无限循环的执行动画。


..addListener((){
      setState(() {
      });


这里监听函数只写了setState,我们前面已经讲过,要更改组件的状态必须通过setState来改变,所以这里必须写入这个,不然组件是不会更改其状态的,当前你也可以通过animation.value获取执行到的当前值。


Curve


在Flutter开发中,通过Curve曲线来描述动画的过程,可以是线性的Curves.linear,也可以是非线性的ono-linear。因此,整个动画过程可以是匀速的,加速的,先加速后减速的等等都行,例如:

CurvedAnimation curve=CurvedAnimation(parent:controller,curve:curves.easeIn);

这里我们创建了一个先快后慢的曲线动画。下面我们定义一个震荡曲线的动画,可以看作弹弹球反过来的动画曲线:

Animation<double> animation;
  AnimationController controller;
  initState(){
    super.initState();
    controller=AnimationController(duration: const Duration(milliseconds: 2000),vsync: this);
    animation=CurvedAnimation(parent: controller,curve: Curves.bounceIn)..addStatusListener((status){
      if(status==AnimationStatus.completed){//动画在结束时停止的状态
        controller.reverse();//颠倒
      }else if(status==AnimationStatus.dismissed){//动画在开始时就停止的状态
        controller.forward();//向前
      }
    })..addListener((){
      setState(() {
        print(animation.value);
      });
    });
    controller.forward();
  }

上面的代码就修改了Tween那句,替换成了CurvedAnimation,这样我们的震荡弹性曲线动画就完成了,Curves.bounceIn可以看成先放大在缩小,在放大在缩小,在放大在缩小,在放大的过程,实现效果如本文首图所示。

相关文章
|
11月前
动画控制器在 Flutter 中的工作原理
【10月更文挑战第18天】总的来说,动画控制器 `AnimationController` 在 Flutter 中起着关键的作用,它通过控制动画的数值、速度、节奏和状态,实现了丰富多彩的动画效果。理解它的工作原理对于我们在 Flutter 中创建各种精彩的动画是非常重要的。
203 60
|
10月前
|
开发工具 UED 容器
Flutter&鸿蒙next 实现长按录音按钮及动画特效
本文介绍了如何在 Flutter 中实现一个带有动画效果的长按录音按钮。通过使用 `GestureDetector` 监听长按手势,结合 `AnimatedContainer` 和 `AnimationController` 实现按钮的动画效果,以及 `flutter_sound` 插件完成录音功能。文章详细讲解了功能需求、实现思路和代码实现,帮助读者逐步掌握这一实用功能的开发方法。
392 5
|
10月前
|
开发者 容器
Flutter&鸿蒙next 布局架构原理详解
本文详细介绍了 Flutter 中的主要布局方式,包括 Row、Column、Stack、Container、ListView 和 GridView 等布局组件的架构原理及使用场景。通过了解这些布局 Widget 的基本概念、关键属性和布局原理,开发者可以更高效地构建复杂的用户界面。此外,文章还提供了布局优化技巧,帮助提升应用性能。
246 4
|
10月前
|
存储 Dart 前端开发
flutter鸿蒙版本mvvm架构思想原理
在Flutter中实现MVVM架构,旨在将UI与业务逻辑分离,提升代码可维护性和可读性。本文介绍了MVVM的整体架构,包括Model、View和ViewModel的职责,以及各文件的详细实现。通过`main.dart`、`CounterViewModel.dart`、`MyHomePage.dart`和`Model.dart`的具体代码,展示了如何使用Provider进行状态管理,实现数据绑定和响应式设计。MVVM架构的分离关注点、数据绑定和可维护性特点,使得开发更加高效和整洁。
386 3
|
10月前
|
前端开发 开发者
深入探索 Flutter 鸿蒙版的画笔使用与高级自定义动画
本文深入探讨了 Flutter 中的绘图功能,重点介绍了 CustomPainter 和 Canvas 的使用方法。通过示例代码,详细讲解了如何绘制自定义图形、设置 Paint 对象的属性以及实现高级自定义动画。内容涵盖基本绘图、动画基础、渐变动画和路径动画,帮助读者掌握 Flutter 绘图与动画的核心技巧。
247 1
|
11月前
|
容器
Flutter&鸿蒙next 布局架构原理详解
Flutter&鸿蒙next 布局架构原理详解
|
前端开发
Flutter快速实现自定义折线图,支持数据改变过渡动画
Flutter快速实现自定义折线图,支持数据改变过渡动画
294 4
Flutter快速实现自定义折线图,支持数据改变过渡动画
|
11月前
|
UED
flutter:动画&状态管理 (十三)
本文档介绍了Flutter中`animatedList`的使用方法和状态管理的示例。`animatedList`用于创建带有动画效果的列表,示例代码展示了如何添加、删除列表项,并执行相应的动画效果。状态管理部分通过一个简单的点击切换颜色的示例,演示了如何在Flutter中管理组件的状态。
138 0
|
Dart JavaScript Java
flutter 架构、渲染原理、家族
flutter 架构、渲染原理、家族
267 3