Flutter-动画-原理篇
一、动画关键类的源码分析
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-动画-原理篇的更多相关文章
- OpenGL10-骨骼动画原理篇(3)-Shader版本代码已经上传
视频教程请关注 http://edu.csdn.net/lecturer/lecturer_detail?lecturer_id=440 接上一个例程OpenGL10-骨骼动画原理篇(2),对骨骼动画 ...
- OpenGL10-骨骼动画原理篇(2)
接上一篇的内容,上一篇,简单的介绍了,骨骼动画的原理,给出来一个 简单的例程,这一例程将给展示一个最初级的人物动画,具备多细节内容 以人走路为例子,当人走路的从一个站立开始,到迈出一步,这个过程是 一 ...
- OpenGL10-骨骼动画原理篇(1)
视频教程请关注 http://edu.csdn.net/lecturer/lecturer_detail?lecturer_id=440 本例程展示如何建立骨骼动画,有些人叫蒙皮动画 定义如下: 当前 ...
- 转:Flutter动画二
1. 介绍 本文会从代码层面去介绍Flutter动画,因此不会涉及到Flutter动画的具体使用. 1.1 Animation库 Flutter的animation库只依赖两个库,Dart库以及phy ...
- Unity3D 骨骼动画原理学习笔记
最近研究了一下游戏中模型的骨骼动画的原理,做一个学习笔记,便于大家共同学习探讨. ps:最近改bug改的要死要活,博客写的吭哧吭哧的~ 首先列出学习参考的前人的文章,本文较多的参考了其中的表述: 1. ...
- 转:Flutter动画一
1. 动画介绍 动画对于App来说,非常的重要.很多App,正是因为有了动画,所以才会觉得炫酷.移动端的动画库有非常的多,例如iOS上的Pop.web端的animate.css.Android端的An ...
- Flutter 动画详解(一)
本文主要介绍了动画的原理相关概念,对其他平台的动画做了一个简要的梳理,并简要的介绍了Flutter动画的一些知识. 1. 动画介绍 动画对于App来说,非常的重要.很多App,正是因为有了动画,所以才 ...
- 《Flutter 动画系列一》25种动画组件超全总结
动画运行的原理 任何程序的动画原理都是一样的,即:视觉暂留,视觉暂留又叫视觉暂停,人眼在观察景物时,光信号传入大脑神经,需经过一段短暂的时间,光的作用结束后,视觉形象并不立即消失,这种残留的视觉称&q ...
- 微信技术分享:微信的海量IM聊天消息序列号生成实践(算法原理篇)
1.点评 对于IM系统来说,如何做到IM聊天消息离线差异拉取(差异拉取是为了节省流量).消息多端同步.消息顺序保证等,是典型的IM技术难点. 就像即时通讯网整理的以下IM开发干货系列一样: <I ...
- 《Flutter 动画系列》组合动画
老孟导读:在前面的文章中介绍了 <Flutter 动画系列>25种动画组件超全总结 http://laomengit.com/flutter/module/animated_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[] ...
- 小D课堂 - 新版本微服务springcloud+Docker教程_6-02 springcloud网关组件zuul
笔记 2.SpringCloud的网关组件zuul基本使用 简介:讲解zuul网关基本使用 1.加入依赖 2.启动类加入注解 @EnableZuulProxy 默认集成断路器 ...
- Linux新增开放端口
CentOS系统 开放端口的方法: 方法一:命令行方式 1. 开放端口命令: /sbin/iptables -I INPUT -p tcp --dport 8080 -j ...
- Introduction to statistical learning:with Applications in R (书,数据,R代码,链接)
http://faculty.marshall.usc.edu/gareth-james/ http://faculty.marshall.usc.edu/gareth-james/ISL/
- Linux系统管理_主题02 :管好文件(1)_2.3 拷贝、剪切、删除和创建文件_cp_mv_rm
用法:cp [选项]... [-T] 源文件 目标文件 或:cp [选项]... 源文件... 目录 或:cp [选项]... -t 目录 源文件... 将源文件复制至目标文件,或将多个源文件复制至目 ...
- 1.ini读写操作
一.使用 TIniFile implementationvar ini:tinifile; path:string; {ini文件路径} section,key:string;{表示 ...
- .Netcore 2.0 Ocelot Api网关教程(3)- 路由聚合
在实际的应用当中,经常会遇到同一个操作要请求多个api来执行.这里先假设一个应用场景:通过姓名获取一个人的个人信息(性别.年龄),而获取每种个人信息都要调用不同的api,难道要依次调用吗?在Ocelo ...
- C学习笔记-字符串的格式化输出和输入
存储方式 字符串是内存中一段连续的char空间,以'\0'结尾 字符串就是0结尾的连续char的内存 '\0' <=> 0 <=> null printf函数,putchar函 ...
- createjs小游戏开发总结
createjs缩放,html组件input的缩放,transform位置,缩放,字体的大小. 移动端横屏和竖屏幕旋转屏幕自适应,input弹出软键盘的原理和位置偏移,focus,blur弹出软键盘. ...
- 随笔--第一次使用crontab linux选择编辑器问题
第一次使用crontab 时,会出现:no crontab for root - using an empty one “Select a editor ......”下面有几个选项,就是叫你选择编辑 ...