第 39 章 D3
39.1 D3.js 简介
D3.js 是一个可以基于数据来操作文档的 JavaScript 库。可以帮助你使用 HTML, CSS, SVG 以及 Canvas 来展示数据。D3 遵循现有的 Web 标准,可以不需要其他任何框架独立运行在现代浏览器中,它结合强大的可视化组件来驱动 DOM 操作。作者是美国计算机科学家和数据可视化专家 Mike Bostock,曾在纽约时报新媒体部门就职多年,其在2011年根据工作需要创作了D3js,《纽约时报》称他为“数字超级巨星”。在纽约时报工作期间,他领导复杂的数据可视化项目,帮助纽约时报获得了2013、2014和2015年Gerald Loeb图像/视觉奖。杰拉德·勒布奖(Gerald Loeb Award),是一个以鼓励记者通过商业,金融和经济领域报道保护私人投资者和公众为目的的奖项。该奖项成立于1958年,主要表彰杰出的商业和金融新闻报道。
在D3.js官网,可以看到许多可交互的案例,能为我们学习提供资源和动力。Mike Bostock 特别执着于案例创作和分享,他曾说:
examples are about demonstrating the potential value of ideas.
学习D3,了解了必要的基础知识后,就应该阅读并熟悉官方英文手册,鉴于准确性和时效性的原因,并不建议阅读网络上存在的各种中文手册。
39.2 D3.js必要基础
如果使用原始JavaScript,我们要在页面中添加一个元素,通常的做法是:
let div = document.createElement("div");
div.innerHTML = "Hello, world!";
document.body.appendChild(div)
39.2.1 链式操作
使用D3插入一个节点的做法是:
是不是更加流畅和易读?人们把这种源于jQuery之类的早期JavaScript库的操作方式叫作链式操作,
39.2.2 绑定数据
let data = [4, 8, 15, 16, 23, 42]
d3.select("body")
.selectAll("p")
.data(data)
.enter()
.append("p")
.text("New paragraph!");
怎么能选择根本就不存在的元素呢? 因为根本就还不存在任何段落,它会返回一个空选择。将这个空选择对应于”将会现身”的很多段落。解决这个问题的关键在于enter()
方法,这是真正的魔法所在。
.data(data) — 对数据值进行计数和解析。我们的数据集中有5个值,所以通过此处的代码都会被执行5次,分别对应于5个值。
.enter()
— 为了生成新的绑定了数据的元素,你必须使用enter()
方法。此方法比较DOM元素和待处理数据个数。如果数据值的个数多于相应的DOM元素,则enter()
方法会生成新的占位元素,在每个占位元素上进行余下的操作。每个占位元素都会被传递给链中下一个方法。
.append("p")
— 接收由enter()
选择的(新生成的)点位元素,然后在DOM中插入一个p元素。新p元素的引用会被传递给链中下一个方法。
.text("New paragraph!")
— 接收新生成的p元素的引用,然后插入(覆盖)文本。
数据绑定到哪里去了?为什么看不到数据?D3将数据绑定到一个元素上时,该数据并没有在DOM中出现,而是作为元素的__data__
属性而存在于内存中。重要的是,可以通过console查看。如果想要数字显示出来,可以通过.text(function (d) { return d; })
能够使用这种语法的原因在于,text(), attr()等许多D3方法支持将一个函数作为一个参数。
39.2.3 d3-selection
Selections 允许对文档对象模型(DOM)进行强大的数据驱动转换:设置属性、样式、属性、HTML或文本内容,
d3.selectAll(“p”) .attr(“class”, “graf”) .style(“color”, “red”); .text(‘hello’)
39.2.4 用d3生成svg
var w = 500;
var h = 50;
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
var dataset = [5, 10, 15, 20, 25];
var circles = svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle");
circles.attr("cx", function (d, i) {
return (i * 50) + 25;
})
.attr("cy", h / 2)
.attr("r", function (d) {
return d;
});
39.3 制作柱状图
39.3.1 准备工作
- 准备HTML
- 引入d3.js
39.3.3 设定图表大小
39.3.4 准备数据
39.3.5 在svg中加入到容器中
39.3.6 在svg中加入g元素作为子容器
39.3.7 添加表头
39.3.8 创建比例尺
// 获取x轴的数据
var Xdatas = data.map(function (d) {
return d.name;
});
// 获取y轴的数据
var values = data.map(function (d) {
return d.value;
});
// 创建X,Y轴比例尺
var xScale = d3.scaleBand().domain(Xdatas).rangeRound([0, width]).padding(0.1),
yScale = d3.scaleLinear().domain([0, d3.max(values)]).rangeRound([height, 0]);
其中padding()
用来设定比例尺分段的间隔,值的范围必须在 [0, 1]
之间
39.3.9 添加x轴和y轴
39.3.11 添加数据
39.3.12 添加说明文字
chart.append('text')
.attr('class', 'barText')
.attr('x', function (d) {
return xScale(d.name);
})
.attr('y', function (d) {
return yScale(d.value);
})
.attr('dx', xScale.bandwidth() / 2)
.attr('dy', 20)
.attr('text-anchor', 'middle')
.text(function (d) {
console.log("d.value", d.value)
return d.value;
});
39.3.13 添加hover事件
39.3.14 设置样式
<style>
body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 14px;
}
.bar {
border: 1px solid #0b3536;
border-radius: 6px;
fill: #0098d8;
}
#bar .barText {
fill: #f5faf8;
font-weight: 500;
}
.axisY text,
.axisX text,
.headerText text {
fill: #0b3536;
}
#bar svg {
background-color: #f5faf8;
}
</style>
39.5 选集selection
在D3中,我们用d3.select方法来选取单个元素。该select方法使用CSS3选择器字符串或者操作对象的引用作为参数,并返回D3选集。随后,用级联修饰函数对该选集的属性、内容以及HTML进行操作。这个选择器也可以用来选择多个元素,但是最终只返回选集中第一个匹配的元素。
d3.selectAll("div") // <-- A
.attr("class", "red box") // <-- B
.each(function (d, i) { // <-- C
d3.select(this).append("h1").text(i); // <-- D
});
第C行定义了一个参数为d、i的迭代函数。第D行则更加有趣,在开头部分,d3.select函数将this封装为一个d3选集,这个选集是一个用变量this表示的且包含当前DOM元素的单元素选集。