Skip to content

深度解析 AntV 技术栈动画的工程原理

概述

目前 AntV 的动画起到的作用主要是增强表现力(类似于样式一般的存在),但对于可视化内容的表达几乎没有实质的影响,其在整个可视化的过程中扮演着一个配角的地位。但随着可视化的发展,随着我们探索的逐步深入,在可预见的未来里,动画的表达将有可能会有更深层次的含义,其在可视化过程中的地位亦将发生翻天覆地的变化的。本文主要从工程角度介绍 AntV 动画的基本原理及现阶段存在的问题,为今后可能迎来的动画改造踏出一小步。

概念列表

名词解释
帧 (Frame)在视频领域,电影、电视、数字视频等可视为随时间连续变换的许多张画面,而帧是指每一张画面。
帧率 (Frame rate)帧率是用于测量显示帧数的量度。测量单位为“每秒显示帧数”(Frame per Second,FPS)或“赫兹”,一般来说FPS用于描述视频、电子绘图或游戏每秒播放多少帧。
帧数 (Frames)帧数为帧生成数量的简称
插值 (Interpolation)通过已知的、离散的数据点,在范围内推求新数据点的过程或方法。

动画定义

动画的基本原理是"视觉暂留"的生理学现象和"相似"的心理学现象。人眼看到物体后,其影像会在视网膜上短暂地停留一会儿(一般认为1/24秒内不会消失),再加上大脑要完成一个感知的过程,当一系列图像变化非常细微,且变化速度非常快时(每秒至少24帧画面),他们看起来似乎混合在一起了,从而构成一种运动的视觉假象。—— 《计算机软件技术基础》

上面那么长的原理,其实一言以蔽之:一张张细微差别的图片固定时间间隔以幻灯片的形式播放,再经过人脑的补完,就是咱们人类所认知的计算机动画。

动画种类

在我们日常工作中我们常常能看到能动的可视化作品,在用户看来能平滑变动,都能称之为动画。但从工程角度看,动画也许应该被分为以下两类:

基于模拟器的 —— 模型动画

图片来源:https://medium.com/@EvanSinar/use-animation-to-supercharge-data-visualization-cd905a882ad4

这类动画,不同时间点,元素的属性(如位置)是由一种模型确定的。在可视化领域内,最常见的模型是物理学领域内的力导模型。这种动画类型的特点是单个图形对象属性的确定往往受环境和自身属性的共同作用。优点是天然符合人的直觉,大多数情况下的动画效果都让人感觉非常自然。缺点是往往性能开销较大,对(GPU)并行计算有非常强的需求,开发难度大,控制性弱。

这类的动画 AntV 目前还探索的比较少,认识也比较初级(仅有的几个案例就是 G6 场景下的动态力导关系图)。这里暂时不展开讨论。

基于关键帧的 —— 补间动画

图片来源:http://www.beautifulinenglish.com/

这类动画,是指再给定前后两个关键帧,系统根据时间进行插值,从而计算出两个关键帧间不同时刻的补间帧,然后逐帧绘制,从而制作出动画效果。这类动画的特点是,对起始帧和结束帧的要求非常严格,尤其是结束帧,如果结束帧不是给定的关键帧将直接导致可视化结果出错。优点是控制性非常强,不同的开始时间、补间时长、插值过程,使我们能设计、制作出千变万化的动画效果,缺点是得需要精细的设计、良好的审美以及极大耐心(要调动画参数)。做这类动画往往对制作者除了工程能力有要求,还需要过硬设计能力,否则制作出的动画将很有可能是反人类的。

补间动画是目前 AntV 技术栈使用最广的动画类型,下面我们将重点展开讨论。

AntV 的补间动画机制

动画机制直接决定了工程上动画的接口以及表现,应是开发者最为关注的模块。所谓补间动画,早年如果使用过 Flash 开发过动画的朋友应该能相对轻松、形象的理解。对于补间动画来说有两个最至关重要的元素,一个是时间轴(下图刻度部分),另一个是补间(下图蓝色部分)。

图片来源:http://flashjournalism.com/tutorials/animation_examples/index.html

若转化到程序,时间轴(Timeline)可以理解成用于执行动画的定时器,而补间(Tween)是可以理解为动画执行对象及执行参数所构成的数据集。

单个执行单个补间动画

要对一个图元做一个补间动画,至少要处理两件事情:时间与插值。时间意味着我们要确定一个补间的起始时间(StarTime)、持续时间(Duration),插值是指根据一段补间起始和结束的属性,确定这段补间不同时刻同一属性的值。举个例子:

上面这个例子描述了对一个圆的横坐标 x 做 持续 2s 的补间动画,代码表示如下:

ts
circle.animate({
  x: 126
}, 2000)

执行多个补间动画

考虑单个图形的单个补间,老实说比简单,一个定时器,一个插值函数就搞定了。但如果是多个补间,问题就稍微复杂了,需要我们再多思考一些东西了。根据历史经验看,执行多个补间动画有两点至关重要:

  1. 使用一个时间轴。
  2. 同一个图形对象同一时间下,执行顺序。

第一点比较简单,意味着整个动画过程中我们始终只有一个定时器(Timer),否则多定时器的话你会遭遇十分严重的性能问题。第二点可能理解起来比较费劲,且未必有标准答案。下面举例说明:

对于前两种补间我们心中可能是自然而然的,没有疑问的,但对于第三种情况,我们可以要稍花心思多考虑一下。最自然的想法是,让两个补间按顺序执行,后面的补间覆盖前面的补间。目前上面那种情况并没有什么问题,但如果是下面这种情况呢?

两个补间运行完以后,图形的终态应该是什么样的呢?从 Flash 这种纯动画制作工具看,可能每个补间互相独立是更合理的。但从 AntV 的动画接口和预期上看。动画只不过是一个中间过程,它应不能影响最终结果的展示。怎么理解?意味着图形的终态只跟动画调用的起始实际有关,跟时长无关。这意味更合理的做法应该是,下一个补间到来时,应该废弃上一补间中相同属性的补间,即废弃掉红色这部分这部分补间。

以上两个最基础的动画实现在 AntV 技术栈中,对于 G2 和 G6 ,是在 G 这个层面完成的。F2 目前是自己独立的一套渲染和动画,细节策略有些有所不同,但整体思路是一脉相承的。

关键帧补间动画

关键帧动画的根本问题是,现有两个画面,怎样让两个画面用动画平滑的过渡。根据 G2 动画文档 的描述,在 G2 中有入场动画(Appear)、出现动画(Enter)、更新动画(Update)、退场动画(Leave),四种动画。那么我们又是通过什么方式去区分这几种动画的呢?

其中入场动画,在 G2 中就是特指第一次渲染(Render)时的出现动画。而剩下三个,则需要基于套比对机制,如下图所示:

现有问题

目前看来,底层引擎 G 层面的动画没有什么问题,主要的问题还是出在上层框架的关键帧动画上。抛除一些接口设计,概念理解上不清晰的问题之外主要还有以下几处硬伤:

没有以当前帧作为动画的起始帧,导致画面闪烁

如果在上一次动画没结束,插入下一个动画,画面会闪烁。增加动画时长,我们发现原因是,下一个动画的起始帧,是上次渲染的结束帧,而不是当前帧。

全量比对性能开销大

这点主要是针对 G6 而言的,由于 G6 中有很多接口是差量(如:增(add)、删(delete)、改(update)),但目前每次都是全量比对,会造成不必要的性能浪费。

无法自定义插值过程

目前是无法自定义动画的插值过程的,使得一些更精细的效果无法完成,比如想让下面数据变化时保留整数,目前是没有接口自定义这件事情的。

声明