# Flutter 动画详解(二)

栏目: IOS · 发布时间: 7年前

内容简介:本文会从代码层面去介绍Flutter动画,因此不会涉及到Flutter动画的具体使用。Flutter的animation库只依赖两个库,Dart库以及physics库。animation是采用Dart编写的,所以依赖Dart库是很正常的。physics库是什么呢?physics库是一个简单的物理模拟的库,包含弹簧、阻尼、重力等物理效果。前篇文章介绍过Flutter动画,Flutter动画两个分类中的一个就是基于物理的动画(Physics-based animation)。所以可以猜测出animation库

本文会从代码层面去介绍Flutter动画,因此不会涉及到Flutter动画的具体使用。

1.1 Animation库

Flutter的animation库只依赖两个库,Dart库以及physics库。animation是采用Dart编写的,所以依赖Dart库是很正常的。physics库是什么呢?

Simple one-dimensional physics simulations, such as springs, friction, and gravity, for use in user interface animations.

physics库是一个简单的物理模拟的库,包含弹簧、阻尼、重力等物理效果。前篇文章介绍过Flutter动画,Flutter动画两个分类中的一个就是基于物理的动画(Physics-based animation)。所以可以猜测出animation库中有一部分代码,是实现了另一种动画--补间动画(Tween Animation)。

通过这种库的划分,也可以大致猜测出,基于物理动画的库是后续添加的。这说明了什么呢?

  • 补间动画是现代移动端相对基础的动画类型,这个是必须的;
  • 基于物理动画是在体验上的改善添加上去的,大致可以猜测出为iOS端的体验优化;

1.2 Physics库

Flutter基于物理的动画,实际上是相当简单的。目前实现了弹簧、阻尼、重力三种物理效果,整个库的代码量也不多。详细的代码在下面的部分介绍,在此处,我们先来说下基于物理的动画库的原理。

基于物理的动画,给我们的感觉会更真实,这是因为其更符合人们日常生活的感官。例如做一个球体下落的动画,如果是匀速的下落,给人的感觉会不够真实,实际的生活经验告诉我们,球体自由下落应该是会有先慢后快的一个过程。如果让我们自己去实现这么一个动画效果,我们会怎么去处理呢?

高中物理我们学习过自由落体相关的概念,其中的位移计算公式:

s = 1/2 * g * t * t

从公式中我们知道,自由落体的位移跟时间不是线性关系。我们可以根据这个公式,来实时的计算出位移来。

如果是摩擦阻尼或者弹簧呢,也都有相关的物理公式,我们所谓的基于物理的动画库,也就是基于此类公式来实现的,本质上还是补间动画,只不过过程遵循物理规律比较复杂罢了。

2. Animation库

讲解这一部分,也考虑过将Flutter的动画原理先介绍一下。想了想,前一篇文章介绍过这些普世的动画原理,Flutter只不过是特定平台的实现,无非是实现手段的不同,因此,Flutter动画原理的解析,放到本文最后的小节部分,在代码的基础上去解释,笔者觉得更加好理解。

2.1 animation.dart

animation.dart定义了动画的四种状态,以及核心的抽象类Animation。

2.1.1 动画的四种状态

这个文件中定义了Animation的四种状态:

  • dismissed:动画的初始状态
  • forward:从头到尾播放动画
  • reverse:从尾到头播放动画
  • completed:动画完成的状态

2.1.2 Animation类

Animation类是Flutter动画中核心的抽象类,它包含动画的当前值和状态两个属性。定义了动画的一系列回调,

  • 动画过程中值变化的回调:
void addListener(VoidCallback listener);
void removeListener(VoidCallback listener);
复制代码
  • 状态的回调函数:
void addStatusListener(AnimationStatusListener listener);
void removeStatusListener(AnimationStatusListener listener);
复制代码

2.2 curve.dart

A curve must map t=0.0 to 0.0 and t=1.0 to 1.0.

看到这段英文,首先会想到什么?没错,插值器。Curve也是一个抽象类,定义了时间与数值的一个接口。

double transform(double t);
复制代码

例如一个线性的插值器,实现代码如下。

class _Linear extends Curve {
  const _Linear._();

  @override
  double transform(double t) => t;
}
复制代码

该文件下面定义了非常多类型的插值器,具体的实现不一一展开了。Flutter定义了一系列的插值器,封装在Curves类中,有下面13种效果。

  • linear
  • decelerate
  • ease
  • easeIn
  • easeOut
  • easeInOut
  • fastOutSlowIn
  • bounceIn
  • bounceOut
  • bounceInOut
  • elasticIn
  • elasticOut
  • elasticInOut

如果上面的13种还不满足需求的话,还可以使用Cubic类来进行自定义的构造。可以看出这块儿实现参考了web中的相关实现。

2.3 tween.dart

该文件定义了一系列的估值器,Flutter通过抽象类Animatable来实现估值器。关于Animatable,我们可以先看下其定义。

An object that can produce a value of type T given an [Animation] as input.

可以根据不同的输入,产出不同的数值。通过重载下面的函数来产生不同的估值器。

T transform(double t);
复制代码

它的最主要的子类是Tween,一个线性的估值器,实现如下,非常的简单,就是一个线性函数。

T lerp(double t) {
  assert(begin != null);
  assert(end != null);
  return begin + (end - begin) * t;
}
  
@override
T transform(double t) {
  if (t == 0.0)
    return begin;
  if (t == 1.0)
    return end;
  return lerp(t);
}
复制代码

在Tween的基础上实现了不同类型的估值器。

  • ReverseTween
  • ColorTween
  • SizeTween
  • RectTween
  • IntTween
  • StepTween
  • ConstantTween

还可以通过自定义的插值器去实现估值器,例如通过curve实现的估值器CurveTween。

2.4 animation_controller.dart

动画的控制,就在这个文件下面实现,其中最重要的部分是AnimationController,它派生自Animation类。

AnimationController的功能有如下几种:

  • 播放一个动画(forwaed或者reverse),或者停止一个动画;
  • 设置动画的值;
  • 设置动画的边界值;
  • 创建基于物理的动画效果。

默认情况下,AnimationController是线性的产生0.0到1.0之间的值,每刷新一帧就产出一个数值。AnimationController在不使用的时候需要dispose,否则会造成资源的泄漏。

2.4.1 TickerProvider

提到AnimationController必须要先说一下TickerProvider。

An interface implemented by classes that can vend Ticker objects.

TickerProvider定义了可以发送Ticker对象的接口,

Ticker createTicker(TickerCallback onTick);
复制代码

Ticker能干什么呢?

Tickers can be used by any object that wants to be notified whenever a frame triggers.

它的主要作用是获取每一帧刷新的通知,作用就显而易见了,相当于给动画添加了一个动起来的引擎。

2.4.2 AnimationController

现在再次回到AnimationController。上面为什么要先说一下TickerProvider呢,这是因为AnimationController的构造函数中需要一个TickerProvider参数。

结合上面介绍的插值器、估值器以及Ticker回调,AnimationController大致的工作流程,我相信很多人都可以理出来了。

随着时间的流逝,插值器根据时间产生的值作为输入,提供给估值器,产生动画的实际效果值,结合Ticker的回调,渲染出当前动画值的图像。这也是补间动画的工作原理。

# Flutter 动画详解(二)

AnimationController具体的源码不做分析了,可以看到Flutter的动画实现的其实是相当的原始,AnimationController需要一个触发刷新的回调,输出也是值的改变,并不像成熟平台里面的配合View去做动画。

3. Physics库

Physics库基本上就是插值器的实现部分,这部分比较简单

# Flutter 动画详解(二)

Simulation定义了基于物理动画的相关接口,具体有位置、速度、是否完成以及公差(Tolerance)

double x(double time);
double dx(double time);
复制代码

GravitySimulation的实现如下,其中_a加速度,_x是初始距离,_v是初始速度:

@override
double x(double time) => _x + _v * time + 0.5 * _a * time * time;

@override
double dx(double time) => _v + time * _a;
复制代码

相信学过高中物理的读者,对这公式不会陌生。其他几种具体实现不在此处一一展开了哈。如果扩展这个物理动画库的话,也很好去扩展,掌握一些物理公式,就可以去仿照实现了。

4. Ticker

关于动画的驱动,在此简单的说一下,Ticker是被SchedulerBinding所驱动。SchedulerBinding则是监听着Window.onBeginFrame回调。

Window.onBeginFrame的作用是什么呢,是告诉应用该提供一个scene了,它是被硬件的VSync信号所驱动的。

具体可以查看sky_engine下面的window.dart的实现,不做展开了。

5. 小节

本篇文章简单的从代码的层面解析了一下Flutter的动画,更深入的Ticker这块儿,感兴趣的读者可以自行去了解,这块儿涉及到sky_engine下面的代码。

本篇文章,笔者依然试图绕过代码去讲解一些普适性的东西,但是Flutter这块儿代码实现的确实挺简单的,造成的问题是调用起来费劲。

基于物理的动画,我们要知道深层次的是物理公式,有这个基础,我们才可以制作出符合感官的动画效果。其本质也是补间动画,过程可以被计算出来。

可以说的宽泛一些,一般的动画,大部分都是补间动画,如果我们自行去设计一套动画系统,插值器、估值器、驱动部分以及动画的管理部分,这四个模块之间相互协调输出一帧一帧的动画过场。绝大部分平台的动画设计,也都逃不过这些因素,只不过实现的方式各不相同。

如果文中有错误的地方,烦请指正,笔者水平有限,再次感谢。


以上所述就是小编给大家介绍的《# Flutter 动画详解(二)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Developing Large Web Applications

Developing Large Web Applications

Kyle Loudon / Yahoo Press / 2010-3-15 / USD 34.99

As web applications grow, so do the challenges. These applications need to live up to demanding performance requirements, and be reliable around the clock every day of the year. And they need to withs......一起来看看 《Developing Large Web Applications》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

URL 编码/解码
URL 编码/解码

URL 编码/解码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具