Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Learn D3:Animation #97

Open
XXHolic opened this issue Aug 28, 2021 · 0 comments
Open

Learn D3:Animation #97

XXHolic opened this issue Aug 28, 2021 · 0 comments

Comments

@XXHolic
Copy link
Owner

XXHolic commented Aug 28, 2021

引子

Learn D3: Shapes 第六篇,只是英文翻译,可修改代码的部分用静态图片替代了,想要实时交互请阅读原文。

正文

与在纸上绘制的图形不同,计算机图形不必是静态的;就像弗兰肯斯坦的怪物一样,他们可以通过动画活过来!✨

91-1

上面的折线图逐步显示。这个可有可无的效果,应该谨慎使用,因为它会引起注意,但它至少强化了 x 代表时间,并给一个原本枯燥的图表带来了一丝悬念。

这里的代码类似于我们前面看到的轴渲染:我们选择一个 SVG 路径元素,调用一个函数(reveal)来应用转换,最后将该元素嵌入 HTML 模板文本中。

91-2

在我们进入技术细节之前,让我们退一步,更全面地思考一下动画。

动画不是单个图形,而是随时间变化的一系列图形。该序列可以表示为返回给定时间 t 的图形的单元(或函数)。为简单起见,我们通常使用常规时间,其中 t=0 是动画的开始,t=1 是结束。

91-3

我们的单元理论上可以返回给定时间 t任何图形,但时间 t 的图形通常与时间 t+ϵ 的图形相似。帧与帧之间的这种相似性有助于观众跟随观看(在上面,仅 stroke-dasharray 属性设置动画;图形的其余部分保持不变。)因此,连续动画通常由离散关键帧定义,中间帧由插值或补格生成。

来看看 stroke-dasharray 属性。它是两个逗号分隔的数字:第一个是虚线(dash)的长度,第二个是虚线之间的间隙长度。如果虚线长度为零,则该线将不可见;如果虚线的长度与直线一样长,那么直线将不会中断。通过调整虚线长度并使间隙至少与直线一样长,这样我们可以控制画多少直线。我们只需要两个关键帧:零虚线长度和线虚线长度。

为了辅助动画(以及其它用途),D3 提供了插值器。其中最通用的是 d3.interpolate,它接受数字、颜色、数字字符串,甚至数组和对象。给定 startvalue 值,d3.interpolate 返回一个函数,这个函数取一个 0 ≤ t ≤ 1 的时间并返回相应的中间值。

91-4

定义转换时,可以显式指定插值器(如上所述,使用 transition.attrTween )或让 D3 选择(使用 transition.attrtransition.style)。显式指定允许使用更高级的插值方法,例如缩放gamma 校正的 RGB 混合,甚至形状混合

91-5

然而,动画不仅仅是插值:它也是计时。我们需要每秒重画 60 次,并根据实时和动画的期望开始时间和持续时间计算标准化时间 t

到目前为止,我们已经看到了两种计时方法。

第一种依赖于 D3 的变换,创建初始图形,然后开始变换以修改它(插入 stroke-dasharray 数组)。

第二种依赖于 Observable 的数据流,每当引用的 t 发生变化时重新创建图形,并依赖一个 scrubber 进行计时。这比前一种方法效率低,因为图形是在每一帧从头开始创建的,但更容易编写。

Observable 还有另一个控制动画的强大工具:生成器。当生成器单元生成一个值时,其执行将暂停,直到下一个动画帧为止,每秒最多执行 60 次。生成的值可以像整数一样简单,也可以是增量更新的 SVG 元素!

91-6
91-7

鉴于 Observable 中提供了各种有关动画的方法,该使用哪种呢?这要看情况了!

如果图形足够简单,你可以从头开始重新创建每个帧,或者如果实际上你不需要动画转换,则以声明方式编写图形。换句话说,什么也不做!得益于 Observable 的数据流,“静态”图形可以在不更改代码的情况下作出响应、交互或动画。

另一方面,对于性能需要高效增量更新的更复杂的动态图形,使用转换或生成器。

你还可以将各种方法结合起来。下图最初是静态的,但给定 x 域一个转换的 chart.update 方法;当单选值更改时,另一个单元将调用此方法。(此代码使用 d3 选择器而不是 HTML 模板文本编写,但图形结构与前面的示例相同,因此请尝试通过比较推断代码的含义。)

91-8
91-9

通过提供一个或多个更新方法,图表可以有选择地为特定值更改的转换设置动画。如果有任何其它变化,图表将回到被动反应状态,并从头开始重新绘制。

(如果你想知道:你可以在单独的单元中定义更新,而不是将其作为方法暴露。但是,不建议这样做,因为编辑更新代码不会从头开始重新绘制图表,这可能会导致不确定性的行为。😱 通过在图表单元中定义更新,它可以访问在更新过程中持续存在的局部变量。)

这个示例演示了 D3 轴的另一个方便功能:通过切换到 transition.call 而不是 selection.call ,x 轴中的更改现在是动态的而不是瞬时的,并与转换路径同步!

上面的动画只涉及一个元素:图表的线条。如果你想要设置多个元素的动画,该怎么办?如果元素集随着时间的推移而变化,新元素进入旧元素退出,该怎么办?读下去!

Next

附录

如果你对设计有效的动画感兴趣,我强烈建议阅读 Heer 和 Robertson 2007 年的论文 Animated Transitions in Statistical Data Graphics

91-10
91-11

根据源码,去除了平台依赖,提取了主要代码,有以下示例:

参考资料

🗑️

看了下《南方车站的聚会》,时代感很强,印象中看胡歌主要的电影还是蛮少的。

91-poster

This was referenced Aug 28, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant