Flutter 动画浅析

什么是动画?

  • 人类具有“视觉暂留”的特性,人的眼睛看到画面在0.34秒内不会消失。利用这一原理,在一幅画还没有消失前播放下一幅画,就会给人造成一种流畅的视觉变化效果。因此,当一段时间内,连续的对UI进行改变;人的眼睛就会产生视觉暂留,最终看到的就是一个“连续”的动画;对于人眼来说,一般帧率只要超过16FPS,就会比较流畅,超过32FPS就会较为细腻平滑,超过32FPS基本感受不到差别了。


    image

Flutter 动画简述

  • 由于 Flutter 动画中, 每一帧的变化都是需要对UI进行改变,短时间内连续的改变UI输出会非常的消耗资源的,因此在UI系统中,动画的平均帧率是重要的性能指标;Flutter 动画在理想状态下是可以实现60FPS的,这和原生应用能达到的帧率是基本是持平;Flutter 中对动画进行了抽象,主要通过 Animation、Curve、Controller、Tween 配合来完成动画
    • Animation 对象是Flutter动画库中的一个核心类,它生成指导动画的值。
    • Animation 对象知道动画的当前状态(例如,它是开始、停止还是向前或向后移动),但它不知道屏幕上显示的内容。
    • AnimationController 管理 Animation
    • CurvedAnimation 将过程抽象为一个非线性曲线.
    • Tween 在正在执行动画的对象所使用的数据范围之间生成值。例如,Tween 可能会生成从红到蓝之间的色值,或者从 0 到 255。
    • 使用 ListenersStatusListeners 监听动画状态改变。

Animation

Flutter中的动画系统基于 Animation 对象的,widget 可以在 build 函数中读取 Animation对象的当前值, 并且可以监听动画的状态改变。

  • Animation 对象本身和 UI 渲染没有任何关系。Animation 是一个抽象类,它拥有其当前值和状态(完成或停止)。其中一个比较常用的 Animation 类是 Animation<double>Animation 对象是一个在一段时间内依次生成一个区间之间值的类, 对象的输出可以是线性的、曲线的、一个步进函数或者任何其他可以设计的映射, 根据 Animation 对象的控制方式,动画可以反向运行,甚至可以在中间切换方向。Animation 还可以生成除 double 之外的其他类型值,如:Animation<Color>Animation<Size>Animation 对象有状态,在动画的每一帧中,我们可以通过 Animation 对象的 value 属性获取动画的当前状态值。

  • 动画监听

    • 使用 addListener()Animation 添加帧监听器,在每一帧都会被调用。最常见的操作是改变状态后调用 setState() 来触发UI重建。
    • 使用 addStatusListener()Animation 添加 “动画状态改变” 监听器;动画开始、结束、正向或反向(见 AnimationStatus 定义)时会调用状态改变的监听器。

CurvedAnimation

  • CurvedAnimation 将动画过程定义为一个非线性曲线
  • Flutter 中通过 Curve(曲线)来描述动画过程,我们把匀速动画称为线性的(Curves.linear),而非匀速动画称为非线性的
final CurvedAnimation curve =
    new CurvedAnimation(parent: controller, curve: Curves.easeIn);
  • Curves 类定义了许多常用的曲线,我们也可以选择自定义
class ShakeCurve extends Curve {
  @override
  double transform(double t) {
    return math.sin(t * math.PI * 2);
  }
}
  • CurvedAnimationAnimationController 都是 Animation<double> 类型,CurvedAnimation 可以通过包装 AnimationControllerCurve 生成一个新的动画对象 ,通过这种方式来将动画和动画执行的曲线进行关联;

AnimationController

  • AnimationController 是一个特殊的 Animation 对象,在屏幕刷新的每一帧,就会生成一个新的值。它包含动画的启动 forward()、stop()、reverse() 等方法;默认情况下,AnimationController 在给定的时间段内会线性的生成0.0~1.0 的数字,另外在创建 AnimationController 时,需要传递一个 vsync 参数,存在 vsync 时会防止动画的UI不在当前屏幕时消耗不必要的资源 。 例如,下面代码创建一个Animation对象,但不会启动它运行
final AnimationController controller = new AnimationController(
    duration: const Duration(milliseconds: 2000), vsync: this);

注:vsync 对象会绑定动画的定时器到一个可视的 widget,当 widget 不显示时,动画定时器将会暂停,当 widget 再次显示时,动画定时器重新恢复执行,这样就可以避免动画相关UI不在当前屏幕时消耗资源。 如果要使用自定义的State 对象作为 vsync 时,请包含 TickerProviderStateMixin;

注意: 在某些情况下,值(position,指动画的当前值)可能会超出AnimationController 的 0.0-1.0 的范围。例如,fling() 函数允许您提供速度(velocity)、力量(force)、position(通过Force对象)。位置(position)可以是任何东西,因此可以在0.0到1.0范围之外。 CurvedAnimation 生成的值也可以超出0.0到1.0的范围。根据选择的曲线,CurvedAnimation 的输出可以具有比输入更大的范围。例如,Curves.elasticIn 等弹性曲线会生成大于或小于默认范围的值。

  • AnimationController 生成数字的区间可以通过 lowerBoundupperBound 来指定
final AnimationController controller = new AnimationController( 
 duration: const Duration(milliseconds: 2000), 
 lowerBound: 10.0,
 upperBound: 20.0,
 vsync: this
);
  • AnimationController 派生自 Animation<double>,因此可以在需要Animation 对象的任何地方使用。 但是,AnimationController 具有控制动画的其他方法。例如,.forward() 方法可以启动动画。数字的产生与屏幕刷新有关,因此每秒钟通常会产生60个数字,在生成每个数字后,每个 Animation 对象调用添加的 Listener 对象。
  • 当动画开始执行后会开始生成动画帧,屏幕每刷新一次就是一个动画帧,每一个动画帧,会随着动画的曲线来生成当前的 Animation.value,然后根据当前的 Animation.value 去构建UI,当所有动画帧依次触发时,Animation.value 会依次改变,构建的UI也会跟随着依次变化,最终我们可以看到一个完成的动画。 另外在动画的每一帧,Animation 对象会调用其帧监听器,等动画状态发生改变时会调用状态改变监听器。

Tween

  • 默认情况下,AnimationController 对象的范围为 0.0~1.0。如果您需要不同的范围或不同的数据类型,则可以使用 Tween 来配置动画以生成不同的范围或数据类型的值;Tween是一个无状态(stateless)对象,需要 beginend 值。Tween 的唯一职责就是定义从输入范围到输出范围的映射。输入范围通常为 0.0~1.0,但这不是必须的;
final Tween doubleTween = new Tween<double>(begin: -200.0, end: 0.0);
  • Tween 继承自 Animatable<T>,而非 Animation<T>AnimatableAnimation 相似,不是必须输出 double 值。例如,ColorTween 指定两种颜色之间的过渡
final Tween colorTween =
    new ColorTween(begin: Colors.transparent, end: Colors.black54);
  • Tween 对象不存储任何状态,它提供了 evaluate(Animation<double> animation) 方法将映射函数应用于动画当前值。Animation 对象的当前值可以通过 value() 方法取到。evaluate 函数还执行一些其它处理,例如分别确保在动画值为 0.0~1.0 时返回开始和结束状态
  • 使用 Tween 对象,需要调用其 animate() 方法,传入一个控制器对象。
// 需要注意的是 `animate()` 返回的是一个 `Animation`,而不是一个 `Animatable`
final AnimationController controller = new AnimationController(
    duration: const Duration(milliseconds: 500), vsync: this);

Animation<int> alpha = new IntTween(begin: 0, end: 255).animate(controller);
  • 以下示例构建了一个控制器、一条曲线和一个 Tween
final AnimationController controller = new AnimationController(
    duration: const Duration(milliseconds: 500), vsync: this);
final Animation curve =
    new CurvedAnimation(parent: controller, curve: Curves.easeOut);

Animation<int> alpha = new IntTween(begin: 0, end: 255).animate(curve);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容

  • 如果我们想要一些东西动画,我们必须改变大小或改变连续帧中对象的位置。例如,在第1帧中,我们的对象位于位置x,在第2...
    开心人开发世界阅读 2,024评论 0 8
  • 参考来源:https://flutterchina.club/animations/ 动画类型 补间(Tween)...
    _白羊阅读 18,209评论 2 11
  • @RequestMapping注解 常用注解 @RequestParam 范围:方法参数上(value = “表单...
    测试员阅读 191评论 0 0
  • 在我的世界里 你叫出婉转 时光一下子就绿了
    人文漯河阅读 177评论 0 1
  • 前言 Node.js的框架也简单的介绍了下,相信我们需要选择什么框架,你也应该知道了,没错,按照我们的需求,最先考...
    我为峰2014阅读 216评论 0 1