引子
继 Learn D3: Joins 第八篇,只是英文翻译,可修改代码的部分用静态图片替代了,想要实时交互请阅读原文。
-
版本:Published Mar 24, 2020
正文
几乎总是有太多的信息无法合理地“容纳”在图表中。因此,设计不仅仅是决定如何展示某些东西,而是根据我们认为对想象中的读者重要的东西,来决定展示什么和不展示什么。
多亏了计算机,真正的读者现在有了发言权:可以根据读者兴趣的需要定制图表。
然而,这种力量是一把双刃剑。交互性允许读者发现更多信息,但迫使读者参与互动。如果我们不小心,我们可能会将重要的见解隐藏在读者从不点击的控件后面。
如果我们的目的是传达已知的信息,我们应该在考虑交互之前设计一个有效的静态图表。另一方面,如果我们试图探索未知,那么交互可以比编码一个新的图表更快(但也考虑可视化语法探索,如 Vega Lite )!
比较好的互动指南可以参考 Ben Shneiderman 的 information-seeking mantra :
Overview first,zoom and filter,then details on demand.
overview 是图表的初始形式。它的目的不是显示所有数据(这是不可能的),而是提供所有数据的“宏观”视图。overview 是引导读者探索。
zooming 和 filtering 是选择感兴趣主题内容显示的方法。我们以前看到过将图表裁剪到各个年份的控件;还有自由缩放的形式、平移和焦点+上下文。如果我们比较多个时间序列,我们可能需要像多折线图示例中那样进行过滤。另请参见可刷散点图矩阵。
details on demand 允许读者从图表中提取精确值,而不限于视觉近似值。这可以像鼠标提示一样简单。
鼠标提示的一种方法是在 SVG 中不受限制的添加标题元素。鼠标悬停(并短暂等待)查看下面某一天的苹果股价。
上图中,1260 个不可见的矩形元素垂直贯穿图表,水平横跨相邻的数据点(用 d3.pairs 计算)。当鼠标悬停在矩形上时,将显示子 title 元素的文本。因此,显示提示信息的是鼠标 x 位置的函数,最适合于 y 为因变量的时间序列图。
更通用的方法是 Voronoi 覆盖,其中距离鼠标最近的数据点确定为信息提示(为了清晰起见,下面绘制了 Voronoi 图……并且因为它看起来很酷。你通常不会显示它。)
本地鼠标提示有几个缺点:它们的显示速度较慢;移动浏览器不支持它们;而且并不总是很明显哪个数据点与提示信息关联,尤其是在使用 Voronoi 图时。
不过,再多做一点工作,我们就可以定制鼠标提示了。
这里元素的顺序很重要:SVG 不支持 z 顺序,因此要在直线和轴之上绘制信息提示,它必须是最后一个。
虽然本地鼠标提示会自动显示,但自定义信息提示通常需要事件监听。当用户执行操作(例如将鼠标移到元素上)时,浏览器将调用这些函数。上面的侦听器捕获闭包中的数据( a 和 b ),以了解在触发事件时显示什么。
(我们使用 Object.assign ,因为 Observable 的 HTML 模板文本当前不支持事件侦听器作为属性。我们更新的 hyperscript literal 确实支持事件侦听器,所以请考虑使用它。我们计划在不久的将来升级标准库。)
自定义信息提示在下面的类中实现。它提供了用于嵌入的 tooltip.node 属性(SVG g 元素),以及 tooltip.show 和 tooltip.hide 方法,以根据需要更新工具提示。这为信息提示的触发方式提供了一定的灵活性。
上面的图表使用 Observable 的 HTML 模板文本,但是我们可以用 D3 风格实现一个相同的图表。用你喜欢的!
mouseover 事件触发的信息提示可能代价高昂:它们要求图表的每个可悬停区域都有一个单独的元素。对于复杂的图表,你可以通过计算按需悬停的内容来提高性能。
对于一维排序的数据(例如我们这里的时间序列数据),一种特别高效的方法是在 mousemove 事件触发时进行二分查找。
二分查找法将立即返回鼠标左侧的数据点,但出于交互目的,最好找到离鼠标最近的数据点(沿 x )。因此,下面的函数首先对分指定日期,然后检查以下数据点是否更接近。
以上所有示例都演示了本地交互:与图表交互只影响图表本身。Observable 最令人兴奋的方面之一是,语言水平的反应性使一个单元中的交互很容易扩散到笔记本中的任何其它单元,从而实现全局交互!
Observable 中的大多数交互控件都由视图实现。要创建视图,定义返回 HTML input 元素的 viewof 单元。然后,input 的实时值将被提供给笔记本的其余部分。
例如,这里有一个由范围输入控制的数字。
类似地,这里有一个文本输入,可以在你键入时显示其值,还有一个下拉菜单,用于从多个固定选项中选择一个。
(还记得之前我们看到动画生成器的时候吗?这些视图也涉及生成器:当你使用 viewof 操作符时,Observable 隐式创建了一个异步输入生成器,每当你与视图交互时,它都会生成一个新值。)
但是视图并不局限于 HTML input ,它们可以是任何东西!唯一的要求是视图提供一个 value 属性,并在该值变化时触发 input 事件。
甚至图表也可以是视图。下面的图表在鼠标移动时触发合成输入事件。
多亏了 Observable 社区,有许多定制的输入可以复用。有关各种有用的选项,请参见 Jeremy 的 inputs bazaar 。或者对于动画,考虑一个 scrubber 。
在本教程中,我们快速浏览了各种交互技术,包括鼠标提示、事件侦听器、Voronoi 覆盖图、视图,但还有很多我们没有介绍的内容!首先,我们甚至没有提到 D3 的可复用行为:brushing、 zooming、 dragging 。(对于自主学习,请在 D3 图库和收藏中查找示例。)
事实上,既然我们已经涵盖了基本知识,现在是时候回头思考下一步该怎么走了。
附录
有关交互的更多信息,请阅读 Gregor Aisch 的 defense of interactive graphics。
附
根据源码,去除了平台依赖,提取了主要代码,有以下示例: