一、动画关键类的源码分析

1、Animation

Animation没有做什么与动画有关的事情,它只是记录了动画的“状态”、当前的“值”和一些注册回调接口的方法。

abstract class Animation<T> extends Listenable implements ValueListenable<T> {

  const Animation();

  // "值"变化的回调
@override
void addListener(VoidCallback listener); @override
void removeListener(VoidCallback listener);
// 状态回调
void addStatusListener(AnimationStatusListener listener); void removeStatusListener(AnimationStatusListener listener);
// 动画状态
AnimationStatus get status;
// 动画当前“值”
@override
T get value; bool get isDismissed => status == AnimationStatus.dismissed; bool get isCompleted => status == AnimationStatus.completed; //... }

2、Tween

Tween记录了一个区间的begin和end。举个例子来说:begin=100   end=200

    • t=0.1  lerp() = 110
    • t=0.5  lerp() = 150
    • t=1.0  lerp() = 200
class Tween<T extends dynamic> extends Animatable<T> {
//...
// 起始值
T begin;
// 结束值
T end; // 计算在特定区间某个时刻的返回值
// t是[0.0,1.0]在某个时刻的比例系数
@protected
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的时候,必须调用Tween.animate()方法。其animate()方法是Tween继承自Animatable<T>类而来的。

// 创建一个Animation
anim = Tween<Offset>(begin: Offset(0, 0),end: Offset(0, 2),).animate(_controller);
// 位移Tween类中,继承自Animatable<T>类
Animation<T> animate(Animation<double> parent) {
return _AnimatedEvaluation<T>(parent, this);
} class _AnimatedEvaluation<T> extends Animation<T> with AnimationWithParentMixin<double> {
//...
@override
final Animation<double> parent;
// 这个变量就是Tween
final Animatable<T> _evaluatable; @override
T get value => _evaluatable.evaluate(parent);
//...
}

由此可以得出animation.value的值来自Tween.evaluate().

3、AnimationController

AnimationController的实现相比较其它的动画核心类来说会比较复杂。那本文章就从两个方面简单分析一下AnimationController的实现。

    • AnimationController的声明
      • AnimationController实现了两个比较重要的Mixin,一个动画值变化的Listener(AnimationLocalListenersMixin),另一个是动画状态改变的Listener(AnimationLocalStatusListenersMixin)。这里就不帖两个Listener的源代码了,感兴趣的可以去Flutter SDK看一下。
// AnimationController的声明
class AnimationController extends Animation<double>
with AnimationEagerListenerMixin, AnimationLocalListenersMixin, AnimationLocalStatusListenersMixin {
    • AnimationController的构造函数

AnimationController可以接收很多参数,其中最重要的就是@required TickerProvider vsync,因为它才是动画动起来的根本原因(下面会讲)。

AnimationController({
double value,
this.duration,// 动画执行时间
this.reverseDuration,// 动画反向执行时的时间长度(默认和duration相同)
this.debugLabel,//debug模式下使用的标签
this.lowerBound = 0.0,// 动画执行完一遍回到的值
this.upperBound = 1.0,// 动画完成的值
this.animationBehavior = AnimationBehavior.normal,// 动画行为(一遍还是重复执行)
@required TickerProvider vsync,
}) : assert(lowerBound != null),
assert(upperBound != null),
assert(upperBound >= lowerBound),
assert(vsync != null),
_direction = _AnimationDirection.forward {
_ticker = vsync.createTicker(_tick);// 计时器
_internalSetValue(value ?? lowerBound);
}

4、CurvedAnimation

CurvedAnimation是一个非线性曲线的Animation,它继承Animation,它与Animation的区别是在取值的时候按照特定非线性曲线函数生成的值。

// 该函数的具体实现方式
@override
double get value {
final Curve activeCurve = _useForwardCurve ? curve : reverseCurve; final double t = parent.value;
if (activeCurve == null)
return t;
if (t == 0.0 || t == 1.0) {
assert(() {
final double transformedValue = activeCurve.transform(t);
final double roundedTransformedValue = transformedValue.round().toDouble();
if (roundedTransformedValue != t) {
throw FlutterError(
'Invalid curve endpoint at $t.\n'
'Curves must map 0.0 to near zero and 1.0 to near one but '
'${activeCurve.runtimeType} mapped $t to $transformedValue, which '
'is near $roundedTransformedValue.'
);
}
return true;
}());
return t;
}
return activeCurve.transform(t);
}

5、SingleTickerProviderStateMixin

我们创建动画的时候,必须要传递一个TickerProvider参数,SingleTickerProviderStateMixin继承TickerProvider。它本身有两个作用:

    • 创建动画计时器。
    • 关联Widget生命周期(实际上给了Ticker)。
@optionalTypeArgs
mixin SingleTickerProviderStateMixin<T extends StatefulWidget> on State<T> implements TickerProvider {
Ticker _ticker;
// 创建Ticker
@override
Ticker createTicker(TickerCallback onTick) {
assert(() {
if (_ticker == null)
return true;
throw FlutterError(
'xxxxxxx'
);
}());
_ticker = Ticker(onTick, debugLabel: kDebugMode ? 'created by $this' : null);
return _ticker;
}
//...
@override
void didChangeDependencies() {
// 关联widget的生命周期,实际上传给了Ticker
if (_ticker != null)
_ticker.muted = !TickerMode.of(context);
super.didChangeDependencies();
}
//...
}

二、动画动起来的过程

这时候我们在外面通过anim.addListener(() {setState(() {});});不断的视图层进行重绘,则控件便动了起来。

三、总结

  • 抽象类Animation仅仅保存了动画状态和回调函数。
  • AnimationController和CurvedAnimation都继承Animation。
  • AnimationController是动画的控制器,控制动画的开始和结束,接收动画执行的时间。
  • Tween用于限定动画的值的区间。
  • SingleTickerProviderStateMixin用于创建Ticker和绑定Widget生命周期。
  • Ticker是一个时间定时器,每一个动画帧每一个动画帧都会调用回调函数。

参考文献:1、感谢Flutter中文网提供的资料。2、感谢简书博主

Flutter-动画-原理篇的更多相关文章

  1. OpenGL10-骨骼动画原理篇(3)-Shader版本代码已经上传

    视频教程请关注 http://edu.csdn.net/lecturer/lecturer_detail?lecturer_id=440 接上一个例程OpenGL10-骨骼动画原理篇(2),对骨骼动画 ...

  2. OpenGL10-骨骼动画原理篇(2)

    接上一篇的内容,上一篇,简单的介绍了,骨骼动画的原理,给出来一个 简单的例程,这一例程将给展示一个最初级的人物动画,具备多细节内容 以人走路为例子,当人走路的从一个站立开始,到迈出一步,这个过程是 一 ...

  3. OpenGL10-骨骼动画原理篇(1)

    视频教程请关注 http://edu.csdn.net/lecturer/lecturer_detail?lecturer_id=440 本例程展示如何建立骨骼动画,有些人叫蒙皮动画 定义如下: 当前 ...

  4. 转:Flutter动画二

    1. 介绍 本文会从代码层面去介绍Flutter动画,因此不会涉及到Flutter动画的具体使用. 1.1 Animation库 Flutter的animation库只依赖两个库,Dart库以及phy ...

  5. Unity3D 骨骼动画原理学习笔记

    最近研究了一下游戏中模型的骨骼动画的原理,做一个学习笔记,便于大家共同学习探讨. ps:最近改bug改的要死要活,博客写的吭哧吭哧的~ 首先列出学习参考的前人的文章,本文较多的参考了其中的表述: 1. ...

  6. 转:Flutter动画一

    1. 动画介绍 动画对于App来说,非常的重要.很多App,正是因为有了动画,所以才会觉得炫酷.移动端的动画库有非常的多,例如iOS上的Pop.web端的animate.css.Android端的An ...

  7. Flutter 动画详解(一)

    本文主要介绍了动画的原理相关概念,对其他平台的动画做了一个简要的梳理,并简要的介绍了Flutter动画的一些知识. 1. 动画介绍 动画对于App来说,非常的重要.很多App,正是因为有了动画,所以才 ...

  8. 《Flutter 动画系列一》25种动画组件超全总结

    动画运行的原理 任何程序的动画原理都是一样的,即:视觉暂留,视觉暂留又叫视觉暂停,人眼在观察景物时,光信号传入大脑神经,需经过一段短暂的时间,光的作用结束后,视觉形象并不立即消失,这种残留的视觉称&q ...

  9. 微信技术分享:微信的海量IM聊天消息序列号生成实践(算法原理篇)

    1.点评 对于IM系统来说,如何做到IM聊天消息离线差异拉取(差异拉取是为了节省流量).消息多端同步.消息顺序保证等,是典型的IM技术难点. 就像即时通讯网整理的以下IM开发干货系列一样: <I ...

  10. 《Flutter 动画系列》组合动画

    老孟导读:在前面的文章中介绍了 <Flutter 动画系列>25种动画组件超全总结 http://laomengit.com/flutter/module/animated_1/ < ...

随机推荐

  1. for(foo('a') ; foo('b') && (i<2);foo('c'))的执行结果

    static boolean foo(char c) { System.out.println(c); return true; } public static void main(String[] ...

  2. 小D课堂 - 新版本微服务springcloud+Docker教程_6-02 springcloud网关组件zuul

    笔记 2.SpringCloud的网关组件zuul基本使用     简介:讲解zuul网关基本使用 1.加入依赖 2.启动类加入注解 @EnableZuulProxy         默认集成断路器 ...

  3. Linux新增开放端口

    CentOS系统 开放端口的方法: 方法一:命令行方式               1. 开放端口命令: /sbin/iptables -I INPUT -p tcp --dport 8080 -j ...

  4. Introduction to statistical learning:with Applications in R (书,数据,R代码,链接)

    http://faculty.marshall.usc.edu/gareth-james/ http://faculty.marshall.usc.edu/gareth-james/ISL/

  5. Linux系统管理_主题02 :管好文件(1)_2.3 拷贝、剪切、删除和创建文件_cp_mv_rm

    用法:cp [选项]... [-T] 源文件 目标文件 或:cp [选项]... 源文件... 目录 或:cp [选项]... -t 目录 源文件... 将源文件复制至目标文件,或将多个源文件复制至目 ...

  6. 1.ini读写操作

    一.使用 TIniFile implementationvar  ini:tinifile;  path:string;       {ini文件路径}  section,key:string;{表示 ...

  7. .Netcore 2.0 Ocelot Api网关教程(3)- 路由聚合

    在实际的应用当中,经常会遇到同一个操作要请求多个api来执行.这里先假设一个应用场景:通过姓名获取一个人的个人信息(性别.年龄),而获取每种个人信息都要调用不同的api,难道要依次调用吗?在Ocelo ...

  8. C学习笔记-字符串的格式化输出和输入

    存储方式 字符串是内存中一段连续的char空间,以'\0'结尾 字符串就是0结尾的连续char的内存 '\0' <=> 0 <=> null printf函数,putchar函 ...

  9. createjs小游戏开发总结

    createjs缩放,html组件input的缩放,transform位置,缩放,字体的大小. 移动端横屏和竖屏幕旋转屏幕自适应,input弹出软键盘的原理和位置偏移,focus,blur弹出软键盘. ...

  10. 随笔--第一次使用crontab linux选择编辑器问题

    第一次使用crontab 时,会出现:no crontab for root - using an empty one “Select a editor ......”下面有几个选项,就是叫你选择编辑 ...