D3.js是JavaScript工具箱中一个相对较新的功能。 数据驱动文档的三个D支架。 您可能已经听说D3只是另一个JavaScript图形库,但这只是部分正确。 的确,D3确实可以产生出色的图形,但是其真正价值在于它能够动态响应数据更改。
在本文中,我们将快速介绍D3,并关注使D3成为基于JavaScript的图形的一种有趣方法的一些基本概念。 我们将查看足够的代码片段,以使您了解D3库的工作方式。
基础
许多人说D3学习曲线很陡,但这全取决于您的观点。 要了解任何库的复杂性可能很困难,但是如果您一直使用jQuery,那么您会获得许多D3中使用的相同概念。 而且,如果您熟悉SVG(可缩放矢量图形)格式,那么您的旅程将会更进一步。
作为示例,请考虑以下D3代码行,看看是否可以猜测它的作用:
d3.selectAll("p").style("color", "red");
如果您猜想它实际上与以下jQuery语句具有相同的作用,请给自己一个拍一下!
$("p").css("color", "red");
selectAll()
函数选择与给定模式匹配的所有元素,而style()
函数对所选内容实施样式更改。
那么D3与jQuery有何不同? 对于初学者来说,它非常擅长动态创建元素-不仅是HTML元素和CSS属性,而且还可以构建和浏览SVG元素。 例如,以下代码选择ID为test
的div
元素,并附加具有特定宽度和高度的SVG元素:
var testBox = d3.select("#test")
.append("svg")
.attr("width", 400)
.attr("height", 150);
此代码在浏览器上刻出一个框,并将其保留给SVG。 注意命令是如何链接在一起的,类似于jQuery。 但是,与jQuery不同,D3中的某些链接命令返回对新元素的引用,而不是对原始选定元素的引用。 在前面的示例中, append()
函数创建一个新的SVG元素并返回对其的引用。 随后的链接命令使用此元素来应用其属性。
现在您已经有了对新SVG框的引用,可以在其中绘制一些内容。
testBox.append("circle")
.style("stroke", "black")
.style("fill", "green")
.attr("r", 50)
.attr("cx", 100)
.attr("cy", 75);
正如您可能已经推论出的那样,先前的代码绘制了一个半径为50的圆,并在坐标空间中偏移了(100,75)。 用黑色描边绘制圆圈,并用绿色填充。
D3-它是数据驱动的!
在实现数据驱动的图形时,D3确实令人赞叹。 不幸的是,这是困难部分开始的地方。 作为D3程序员,您必须了解数据如何进入D3应用程序以及数据到达D3应用程序后的作用。 另外,您必须考虑数据如何离开应用程序。
让我们回到上面创建的testBox
SVG元素。 将此框视为自动调整为放入其中的数据的系统。 数据使用以下三种机制之一与盒子配合使用:
- 数据进入框。
- 框内的数据会更新。
- 数据离开盒子。
可以使用enter()
, update()
和exit()
函数来概括这些概念。
想象一下上面的testBox
作为保持器,以圆圈的形式显示数据。 每个圆圈代表一个数据点,每个数据点具有三个属性。 这三个属性可以表示为x轴上的位置,y轴上的位置和半径。 数据集可能看起来像这样:
var bubbleChart = [[43, 54, 23], [97, 15, 14], [114, 100, 20]];
显然,此示例缺乏真实感。 为了更加现实,我们将数据包含在某种JSON结构中,看起来像真实数据库的输出。 但是,通过坚持使用此三列矩阵,我们将使此示例保持简单。 稍后,我们将在程序运行时在矩阵中添加和删除行。 D3包含一些强大的机制来处理您的数据,包括从外部源查询数据的能力。 当跟踪天气,股市,地震等动态值时,这非常有用。
让我们从上面的testBox
示例开始。 我们将摆脱绘制的圆,而取而代之的是让数据为我们绘制圆。
var bubbleChart = [[43, 54, 23], [97, 15, 14], [114, 100, 20]];
var testBox = d3.select("#test")
.append("svg")
.attr("width", 400)
.attr("height", 150);
var tb = testBox.selectAll("circle").data(bubbleChart);
tb.enter()
.append("circle")
.style("stroke", "black")
.style("fill", "green")
.attr("cx", function(d) { return d[0]; })
.attr("cy", function(d) { return d[1]; })
.attr("r", function(d) { return d[2]; });
您可以在bubbleChart
数组中看到数据的声明,并且testBox
变量testBox
400×150的testBox
雕刻出SVG空间。 数据与SVG的“连接”发生在我们定义tb
变量时:
var tb = testBox.selectAll("circle").data(bubbleChart);
这行看起来很奇怪,因为我们还没有定义任何称为circle
选择,因此最初看起来选择是空的。 嗯,这不是真的,因为随后的data()
函数调用告诉D3将所有圆选择都加入到bubbleChart
数据中。
请记住,在最初运行应用程序时,框中没有数据。 进行连接时, bubbleChart
包含的数据突然“进入”该框。 之后,将调用enter()
函数。 tb.enter()
调用将圆形元素附加到SVG框,并分别使用笔触和填充颜色设置样式。
接下来,为每个圆细分数据结构的各个行。 例如,y位置信息是通过以下attr()
函数调用设置的:
.attr("cy", function(d) { return d[1]; })
该函数有两个参数:要设置的属性的名称(在本例中为y位置)和该属性的值。 由于此元素已与数据结构连接在一起,因此第二个属性包含一个自动在该数据结构的成员上起作用的函数调用。 D3实现了声明性编程风格,因此您实际上无需自己编写循环-数据结构中的每个第一级元素都会调用enter()
函数。 在这种情况下,我们有一个二维矩阵,因此在每次迭代中,都会将一个不同的数组传递给函数调用。 我们要做的就是拉出数组中的各个元素,并使用它们来设置每个圆的x,y和半径。
动力学
到目前为止,我们已经研究了基于数据的图形渲染,但是还没有研究D3的动态方面。 如前所述,数据正在进入,更新或离开系统。 在上面的示例中,具有三列的矩阵表示数据。 D3将该矩阵视为数据,其中矩阵的每一行都是一个附加数据元素。 为了说明数据如何变化,我们必须将上述大多数逻辑封装在一个函数中,然后在每次数据变化时都运行该函数。
例如,每次运行该函数时,我们都会为bubbleChart
的行选择新的随机值。 为了更进一步,每次更改后,我们要么添加行,要么从bubbleChart
删除行。 添加行后,将调用enter()
函数来处理新信息。 删除行后,将调用exit()
函数以确保将其删除。 最后,当元素更改其值时,将调用update()
函数来处理更新的信息。 注意,本身没有update()
函数。 当调用data()
函数将数据与图形元素连接时,它将返回一个指向更新函数的指针。
最终的JavaScript代码出现在下面的列表中。 请注意, update()
函数(简称tb
)将圆圈涂成红色,而enter()
函数将新圆圈涂成绿色。 exit()
函数只是从图形元素中删除圆圈。 还要注意,添加了一个“运行”按钮,以便每次按该按钮都可以生成新数据。
var root = d3.select("#test");
var testBox = root.append("svg")
.attr("width", 400)
.attr("height", 150);
var runCircles = function() {
var bubbleChart = [];
var numCircles = Math.floor(Math.random() * 11); // select 0 - 10 circles
for (var i = 0; i < numCircles; i++) {
bubbleChart.push([Math.floor(10 + Math.random() * 390), Math.floor(10 + Math.random() * 140), Math.floor(10 + Math.random() * 40)]);
}
var tb = testBox.selectAll("circle").data(bubbleChart);
tb.style("stroke", "black").style("fill", "red")
.attr("cx", function(d) { return d[0]; })
.attr("cy", function(d) { return d[1]; })
.attr("r", function(d) { return d[2]; })
.attr("opacity", .5);
tb.enter()
.append("circle")
.style("stroke", "black")
.style("fill", "green")
.attr("cx", function(d) { return d[0]; })
.attr("cy", function(d) { return d[1]; })
.attr("r", function(d) { return d[2]; })
.attr("opacity", .5);
tb.exit().remove();
};
root.append("button").text("run").on("click", runCircles);
在下图中,您可以看到两次后续运行之间发生的情况。 在第一次运行中, bubbleChart
有四个元素,因此屏幕上有四个圆圈。 一个红色圆圈是上一次运行的更新,并且有三个新的数据元素,用绿色表示。
在下一次运行中,前四个元素现在显示为红色。 它们的位置和大小已更改,但仍在更新中,因此以红色显示。 同时,数据库中又添加了四个新元素,显示为绿色。
最后一点,D3提供了一些新颖的方法来动画化数据转换。 因此,上面的示例可能会在更新时将现有图形元素从一个状态淡入淡出和/或移动到另一个状态,而新元素可能会淡入淡出。D3网站上的教程提供了许多令人印象深刻的过渡效果。
结论
D3.js是一个功能强大的JavaScript图形库。 它不仅可以简单地绘制图形,还可以将数据集与一组图形元素结合在一起,并提供一个真正的数据驱动的图形环境。 本文涉及D3的一些主要概念。 尽管D3的学习曲线相当陡峭,但是如果您已经熟悉jQuery和SVG,您会发现D3的学习非常简单。 您可以在D3网站上找到完整的详细信息和许多有用的教程。