第 41 章 D3.js中的数据绑定与更新
卡洛斯·科罗内尔(Carlos Coronel),史蒂文·莫里斯(Steven Morris)和彼得·罗布(Peter Rob)在其名著《Database Systems: Design, Implementation, and Management》中认为:
数据是纯粹的事实。“纯粹”意味着这种事实没有经过任何处理,其意义也没有得到揭示。而信息是数据处理的结果,它揭示了数据的意义。
而数据的可视化更注重人对数据内在观察的展示。而D3能将数据和图形领域联系起来。
41.1 进入—更新—退出模式
D3使用它称为“进入—更新—退出”(enter-update-exit)模式将数据和图形建立联系。进入—更新—退出模式是所有基于D3的可视化技术的基石。
- 进入模式 enter函数将返回一个新的D3对象集合,这个集合包含了所有没有可视化的数据,我们可以在这个集合上级联调用相关函数,创建新的图形来表示给定的数据。这个集合的状态称为进入模式(Enter Mode)。
- 更新模式 data函数的返回值是一个(绑定了数据的)新D3对象集合,也就是数据与可视化图形的交集中的所有元素。现在,我们就可以针对新集合调用相关函数,并对其中的元素进行更新。这个新集合所在的状态通常称为更新状态(Update Mode)。
- 退出模式 当我们在一个拥有数据绑定的D3对象集合上调用selection.data(data).exit函数的时候,会得到一个新的D3对象集合。这个新的集合包含原集合上没有关联任何有效数据的图形。当然,作为一个有效的D3选集对象,我们也可以在这个新的集合上级联调用相关函数以更新这些图形,或者当我们不再需要这些图形时删除它们。这个新的集合状态称为退出模式(Exit Mode)。
除了上述三种模式之外,还有一种合并模式(merge),merge()
函数将传递给merge函数的给定选集与调用函数的选集进行合并,并返回二者的并集作为新的选集。在进入—更新—退出(enter-update-exit)模式中,合并函数通常用于构造一个同时涉及进入模式和更新模式的选集。
41.1.1 理解Update、Enter、Exit
update
和enter
:例如将数组[3,6,9,12,15]
绑定到三个<p>
上。可以想象到,数组的最后两个元素没有可以绑定的元素,这时D3会建立两个空的元素与数组最后的两个数据相对于,那么这部分就称为Enter。而有元素与数据对应的部分就称为Update。
exit:如果数组[3]
绑定到三个<p>
上,可以想象,最后两个<p>
没有可绑定的数据,那么没有数据绑定的部分就称为Exit。
d3.select(document.body).append('p')
d3.select(document.body).append('p')
d3.select(document.body).append('p')
var dataset = [3, 6, 9, 12, 15];
var p = d3.select("body")
.selectAll("p");
var update = p.data(dataset) //绑定数据,并得到update部分
var enter = update.enter(); //得到enter部分
//下面检验是否真的得到对于update的处理
update.text(function(d, i) {
return "update: " + d + ",index: " + i;
})
//对于enter的处理
//注意,这里需要先添加足够多的<p>,然后在添加文本
var pEnter = enter.append("p") //添加足够多的<p>
pEnter.text(function(d, i) {
return "enter: " + d + ",index: " + i;
})
$$.html(document.body.innerhtml)
var dataset = [ 3 ];
//选择body中的p元素
var p = d3.select("body").selectAll("p");
//获取update部分
var update = p.data(dataset);
//获取exit部分
var exit = update.exit();
//update部分的处理:更新属性值
update.text(function(d){
return "update " + d;
});
//exit部分的处理:修改p元素的属性
exit.text(function(d){
return "exit";
});
//exit部分的处理通常是删除元素
exit.remove();
41.2 将数组绑定为数据
使用JavaScript中的数组作为D3可视化中的数据是最常见的做法。例如,你有一个数组,它里面存储了许多数据元素,你希望生成一系列图形元素,其中每一个图形代表数组中的一个记录,并且当数组中的数据进行更新的时候,图形元素也发生相应的改变。
var data = [10, 15, 30, 50, 80, 65, 55, 30, 20, 10, 8];
var bars = d3.select("body").selectAll("div") //宣布页面上应该有一系列div元素,已构成图形集合
.data(data); //将数据绑定到这些将要创建的图形元素上 update
bars.enter() // Enter选出还没有可视化的数据
.append("div") // 为选定的每一个数据创建一个div元素,并将其加入body元素中
.style("background-color", "#ccc")
.style('text-align', 'right')
.style('margin', '2px')
.style("width", function(d) {
return (d * 3) + "px";
})
.text(function(d) {
return d;
});
data = data.map(function(i) {
return i + 3;
});
bars = d3.select("body").selectAll("div")
.data(data);
bars.enter()
.append("div")
.style("background-color", "#ccc")
.style('text-align', 'right')
.style('margin', '2px')
.merge(bars) //对进入模式选中的元素(进入模式选集)和更新模式选中的元素(更新模式选集)进行合并,从而返回二者的并集
.style("width", function(d) {
return (d * 3) + "px";
})
.text(function(d) {
return d;
})
$$.html(document.body.innerHTML);
动态转换方法实际上有d和i两个形式参数。其中,第一个参数d代表与图形元素相关联的数据,这个我们之前已经提过了;第二个参数i是一个从0开始的当前图形元素的下标。
41.3 将对象绑定为数组
比数组更为常见的数组形式,是对象。
var dataObject = [ // <- A
{
width: 10,
color: 23
}, {
width: 15,
color: 33
},
{
width: 30,
color: 40
}, {
width: 50,
color: 60
},
{
width: 80,
color: 22
}, {
width: 65,
color: 10
},
{
width: 55,
color: 5
}, {
width: 30,
color: 30
},
{
width: 20,
color: 60
}, {
width: 10,
color: 90
},
{
width: 8,
color: 10
}
];
var colorScale = d3.scaleLinear()
.domain([0, 100])
.range(["#add8e6", "blue"]); // <- B
function render(data) {
var bars = d3.select("body").selectAll("div.h-bar")
.data(dataObject); // Update
// Enter
bars.enter()
.append("div")
.attr("class", "h-bar")
.merge(bars) // Enter + Update
.style("width", function(d) { // <- C
style("width", function(d) { // <- C
return (d.width * 5) + "px"; // <- D
})
.style("background-color", function(d) {
return colorScale(d.color); // <- E
})
.text(function(d) {
return d.width; // <- F
});
// Exit
bars.exit().remove();
}
function randomValue() {
return Math.round(Math.random() * 100);
}
style("width", function(d) { // <- C
return (d.width * 5) + "px"; // <- D
})
.style("background-color", function(d) {
return colorScale(d.color); // <- E
})
.text(function(d) {
return d.width; // <- F
});
// Exit
bars.exit().remove();
}
function randomValue() {
return Math.round(Math.random() * 100);
}
render(data);
41.4 打开外部数据文件
D3.js 也可以外部数据文件为数据来源,我们可以使用异步编程方式,打开外部数据。