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/ < ...
随机推荐
- nodejs获取常见疾病数据示例
日常生活中有一些常见的疾病,这个可以通过百度等搜索到,但是如果你要完成一款app或者小程序.网站之类的该如何来获取常见疾病的信息呢?首先想到的是通过爬虫爬取数据,然后整理搜索....其实这种方法还是太 ...
- JAVA中注解的实现原理
注解的本质 「java.lang.annotation.Annotation」接口中有这么一句话,用来描述『注解』. The common interface extended by all anno ...
- linux下配置maven并修改maven源
参考文章 <Linux下Maven的安装与使用> <aliyun阿里云Maven仓库镜像地址> <maven国内镜像配置(Ubuntu)> 下载maven,具体目录 ...
- 图文详解 : 什么是版本控制?Eclipse配置SVN和IDEA配置GIT教程
前言 虽然在工作中, VCS已然配置妥当, 我们敲好的业务只需要Commit&push提交就好, 但是不妨碍我们了解什么是版本控制, 为什么要使用这类工具? ps.最近项目里的小伙伴想在自己家 ...
- mac 查看占用的端口并关闭进程
-i 查看某个端口是否被占用,如 lsof -i:8081 显示占用情况 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME java 12188 a ...
- PTA(Advanced Level)1083.List Grades
Given a list of N student records with name, ID and grade. You are supposed to sort the records with ...
- Daily Temperatures
Given a list of daily temperatures T, return a list such that, for each day in the input, tells you ...
- 基于SpringBoot从零构建博客网站 - 整合ehcache和开发注册登录功能
对于程序中一些字典信息.配置信息应该在程序启动时加载到缓存中,用时先到缓存中取,如果没有命中,再到数据库中获取同时放到缓存中,这样做可以减轻数据库层的压力.目前暂时先整合ehcache缓存,同时预留了 ...
- Android的视图(View)组件
Android的绝大部分UI组件都放在android.widget包及其子包.android,view包及其子包中,Android应用的所有UI组件都继承了View类,View组件非常类似于Swing ...
- 【动态规划】Mathematical Curse
[来源]:2018年焦作网络赛B [题意]: 有n个数字,有m个符号运算.通过不回头(即选取m个数有顺序可言),消除巫术的,并达到最大的价值. 其实意思就是在数组里选取一段子序列,然后进行m次加减乘除 ...