读完时间:2021 年 11 月 4 日
出版时间:2020 年 9 月
5.2 绑定数据
数据可视化说到底就是把数据映射成图形——输入数据,输出图形。
D3 绑定的数据没有出现在 DOM 中,而是作为该元素的 data 属性保存在内存里。
可以把 d 想象成一个寂寞的小占位值,它需要一点温暖,包括来自和蔼可亲的函数圆括号的拥抱。
6.1 绘制 DIV
严格来说,柱形图(column chart)指的是沿垂直方向度量的矩形,而沿水平方向度量的矩形叫条形图(bar chart)。不过,大多数人不区分这两个概念,而统一称其为条形图
D3 的另一个方法 classed()
,用于快速地添加或删除元素的类。比如,前面那行代码可以重写成下面这样:
.classed("bar", true)
这行代码会为调用该方法的选集添加类 bar。如果第二个参数是 false,则会把元素的类 bar 删除:
.classed("bar", false)
6.4 绘制条形图
可以使用 SVG 的 text-anchor
属性,让文本在指定的 x 值上水平居中。
.attr("text-anchor", "middle")
第 7 章 比例尺
听到比例尺这个词,有人可能会不由自主地想到最终图表中的一系列刻度线,对应一系列值。不要搞错,这些刻度线是坐标轴的一部分,而坐标轴只是比例尺的一种形象表示。比例尺实际上代表着一种数学关系,没有直接的视觉呈现。大家可以把比例尺和坐标轴想象成两样不同但相关的东西。
7.1 苹果和像素
到目前为止,我们一直都在直接显示数据值,忽略了单位的差异。
7.3 归一化
归一化就是根据可能的最小值和最大值,把某个数值映射为介于 0 和 1 之间的一个新值的过程。比如,一年 365 天,那么 310 天映射过来就是 0.85,即一整年的 85%。
7.4 创建比例尺
这些步骤可以独立完成,也可以用一行代码全部连起来:

var scale = d3.scaleLinear()
.domain([100, 500])
.range([10, 350]);
8.3 定位数轴
如果你在给 SVG 元素应用样式时,发现 CSS 代码根本不起作用,我劝你不要心急。深呼吸,稍等一下,再仔细瞧瞧那些属性名,一定要保证是 SVG 的,而不是 CSS 的。
9.2 更新数据
什么是事件监听器呢?其实也是匿名函数,可以监听(一个或多个)特定元素上发生的事件。D3 的 on()
方法是添加事件监听器的简便方法,它接受两个参数:事件类型("click")和监听器(匿名函数)。
9.3 过渡动画
根据经验,对于细微的界面反馈(比如鼠标悬停在元素上),过渡时间大约 150 毫秒比较合适,而更显著的视觉过渡(比如整个数据视图的变化)持续 1000 毫秒比较理想。1000 毫秒(1 秒)不算长,也不算短。
过渡只能用在已经存在的值上;如果你对不存在的值应用了过渡,会触发意料之外或者你不希望得到的行为。比方说,你想让前面例子中的矩形的不透明度慢慢降低。可以执行这段代码 svg.selectAll("rect").transition().attr("opacity", 0)
。这会让矩形立刻变得透明(0% 的不透明度),没有平滑的过渡效果。如果你一开始设置了 attr("opacity", 1)
,那么过渡才会按预期发生:从开始值(1)过渡到最终值(0)。要让新值产生过渡效果,一定要记得先设置初始值哦。
静态延迟时间只是一种延迟方式,更有意思的是可以动态计算延迟时间。动态延迟的一个常见用途就是创建交错延迟的效果,让某些元素的过渡在其他元素之前发生。交错延迟对人的感知有利,因为当相邻元素的变化不那么同步时,人眼更容易注意到每个元素的变化。
在默认情况下,任何元素在任意时刻都只能有一个过渡效果。新过渡效果会打断并覆盖原来的过渡效果。不过,可以用 on("end", ...)
指定另一个过渡,因为执行 on("end", ...)
时,之前的过渡已经结束了,因此再执行新过渡不会产生任何副作用。
SVG 支持剪切路径(clipping path),也就是 Sketch、Photoshop 和 Illustrator 这类绘图工具中的蒙版。剪切路径就是一个 SVG 元素,可以包含可见的元素,并与这个可见元素一起构成可以应用到其他元素的剪切路径或蒙版。元素应用了蒙版后,只有落在该蒙版形状内部的像素才会显示。
9.4 其他数据更新方式
维护对象恒定性的关键在哪儿?就在键(key)上。(顺便告诉大家,Mike Bostock 关于对象值的恒定性写过一篇很精彩的文章“Object Constancy”,推荐大家读一读。)
只要每个数据都有独一无二的 key,我们就可以灵活地随意添加或删除数据值(和元素)。
这一章讲的内容太多啦!好吧,咱们简单回顾一下。
data()
把数据绑定到元素,但也会返回更新选集。- 更新选集可能包含加入和退出选集,这两个选集可以通过
enter()
和exit()
方法得到。 merge()
一般用来合并加入选集和更新选集,以便同时给两者应用某些改动。- 数据值比元素多的情况下,加入选集会引用尚不存在的占位元素。
- 元素比数据值多的情况下,退出选集会引用没有对应数据的元素。
- 数据聚合决定数据值怎么与元素匹配。
- 默认情况下,数据聚合按照索引进行,也就是数据集里值出现的顺序。
- 想更细致地控制数据聚合,可以指定键函数。
10.1 绑定事件监听器
大多数情况下,事件一直在不同的位置被触发,只是没人监听,它们会自生自灭。
10.2 什么是行为
有些人之所以不喜欢 JavaScript,一个主要原因就是这个关键字 this
。在其他语言中,this
的含义非常明确,不像 JavaScript 中那么多变。
很多时候,我们希望在某些元素上(比如在值标签上)忽略鼠标事件。幸好,只要给要忽略的元素添加一行 CSS 代码即可:
pointer-events: none;
这行神奇的代码会通知浏览器:“嘿,这个元素不要触发任何指针事件(比如 click、mouseover 或 mouseout),就当这个元素不在那儿。”这样事件就可以穿透这个元素,在它下面的元素上触发。
10.4 提示条
只要把鼠标放在任何条形上,过几秒钟就能看到提示条出来。这段相对长的延迟时间是浏览器决定的,不受 D3 和 JavaScript 的控制,所以我们无可奈何。

11.1 折线图
defined()
最简单的用法就是判断给定值是否存在:

.defined(function(d){ return d; })
如上所示,如果 d 存在,函数就返回 true。
然而在我们的例子中,-99.99 确实是个存在的值,只不过我们认为它不是有效的测量值。为了排除它们,可以借助比较操作符,返回逻辑判断的结果(true 或 false):
// 定义线条生成器
var line = d3.line()
.defined(function(d){ return d.average >= 0; })
.x(function(d){ return xScale(d.date); })
.y(function(d){ return yScale(d.average); });
11.2 区域图
我不知道你看了现在的图会有什么感受,但它让我觉得大气中二氧化碳实在太多了。(突然之间,我希望你看的是本书的电子版,因为电子版是用可再生能源来驱动的。如果你读的是砍树造出来的纸质版,也不要太难过,利用从本书学到的知识多做好事就行啦。)
12.2 选择更具体的元素
为了避免这类混淆,Mike Bostock 推荐了一种代码缩进约定:选集没变的时候缩进四个空格,返回新选集时只要两个空格。Mike 在 d3-selection 的文档中提供了一个示例:
select()
和 append()
方法都会返回新选集。attr()
不会,它只是把刚刚修改的选集往后传递。注意,这个示例要想生效,这些元素的父子关系必须满足:body 是 svg 的父元素,svg 是 g 的父元素,g 是 rect 的父元素。
13.2 堆叠布局
d3.stack()
能够把二维数据转换成“堆叠”数据,它会计算每个数据点的基线值,以便把数据层相互堆叠起来。这个布局方法可用于创建堆叠条形图、堆叠面积图,甚至河流图(stream graph,就是没有严格零起点基线值的堆叠面积图)。
15.1 准备数据
- HEV(hybrid electric vehicle,即混合电动汽车,主要用汽油做燃料,也配备有电池和电动机。)
- PHEV(plug-in hybrid electric vehicle,即插电式混合电动汽车,供能方式既可以是电力,也可以是汽油。)
- BEV(battery electric vehicle,即电池电动车,也就是我们说的“纯”电动车。)
- FCEV(fuel-cell electric vehicle,即燃料电池电动车,通常是氢动力燃料电池车。)
附录 A 案例研究
如果你在把玩自己可视化作品上花的时间比写代码的时间更多,那么你的方向就没错。如果你能从自己的作品中看出模式,那也说明你走对了方向。关于如何引起人们的兴趣,我认为如果你能够花一些精力做些调查,收获会很丰富(比如发现某种很有意思的模式)。在这个过程中找到恰当的平衡点,那么人们可能会留下来探索你的作品。
附录 B 4.0 版本新增特性
B.1 模块化
D3 不再是一个庞大、笨重的库了,现在它实现了模块化,也就是说它由许多小型库组成,每个小型库注重一个特定的领域。除非你的项目花样特别多,否则一般来说你只用默认的代码包即可,它包含了下列 D3 微型库和模块(对应的代码仓库都包含了各自的文档):
· d3-array
· d3-axis
· d3-brush
· d3-chord
· d3-collection
· d3-color
· d3-dispatch
· d3-drag
· d3-dsv
· d3-ease
· d3-force
· d3-format
· d3-geo
· d3-hierarchy
· d3-interpolate
· d3-path
· d3-polygon
· d3-quadtree
· d3-queue
· d3-random
· d3-request
· d3-scale
· d3-selection
· d3-shape
· d3-time
· d3-time-format
· d3-timer
· d3-transition
· d3-voronoi
· d3-zoom
附录 C 进阶学习
StackOverflow:当你被某个问题难住时,可以在 StackOverflow 上发布带有 d3.js 标签的问题。不过在你提问之前,请先阅读一份关于如何提一个好问题的重要清单。
(1)用清晰、简洁、明确的语言描述你遇到的情况。
(2)描述你期望达到的目的或者想看到的效果。
(3)先在网上、D3 Google Group 还有 StackOverflow 中搜索,看看有没有人问过相似的问题。
(4)搜索 D3.js 的作品库(D3.js Gallery),看看有没有示例实现了你要的类似效果,并研究这个示例找点头绪。
(5)用 Bl.ock 构造器的搜索工具寻找用到正在困扰你的方法的示例,研究这些示例找点头绪。
(6)如果问题还是没解决,把你的代码发布到能被公共访问到的地方。(查看附录 D 了解怎么快速做到这一点。)这是至关重要的一步,否则别人看不到你的代码,也就爱莫能助。提供别人需要的一切,他们才能更方便地帮你。
(7)最后,把你的问题发到 StackOverflow 上,带上 d3.js 标签,还有指向你的代码的链接。(查看附录 D 了解怎么做。)
D3 相关网站
❑ D3.js Gallery
Christophe Viau 创建的带搜索功能的作品库,可以作为官网的替代。强烈推荐!
❑ Mike Bostock's Blocks
这个网站上有更多的示例(全是 Mike Bostock 写的),每个示例往往会突出 D3 的某个特性。
❑ Bl.ock 构造器搜索工具(Bl.ock Builder)
Ian Johnson 开发的这个工具可以很方便地搜索任何人创建的所有 D3 代码块。(查看附录 D 了解 Bl.ock 构造器的详情。)试着搜一搜你想看示例的 API 方法(如 d3.geoPath()
)。
❑ tributary.io
Ian Johnson 开发的实时编码环境,用来测试 D3 代码。(Ian 怎么一直在做这些有用的工具,能不能停一停?)
最后,Nathan Yau 在他的文章“Getting started with visualization after getting started with visualization”里给出了大量明智的建议和宝贵的提示。
找一份工作动手实践
我的个人网站上有一份现成的列表:Where to Post and Find Data Visualization Jobs。不开玩笑:D3 技能的需求量很大。如果你吃透了本书,你可以找到工作。我还写了“Data Visualization and Art Process Blogs”。我是程序的狂热爱好者,我用这篇博客文章收集了那些才华横溢的人写的好文章链接,这些文章介绍了他们是怎样创作出令人惊叹而优美的作品的。向他们学习,并不断进步。
2021年12月4日
评论在审核吗?
2021年12月4日
我也要看看!
2021年12月4日
这本书挺好!