数据绑定(data join)做为d3的一大特色,极大方便了对数据及元素的操做
哪里方便呢?这里列举一个简单的例子:
假设需求以下:有数据[5, 15, 25, 35],制做一张柱状图,同时添加交互,鼠标移至对应柱子时变色css
若是咱们有数据绑定,咱们将数据绑定到元素,返回selection以后,有如交互、动画、更新等的操做直接经过selection进行便可
若是没有数据绑定,单纯的使用js与div元素生成柱状图,在交互添加,监听更新方面都得使用for循环来添加等等,不但开发效率低,并且代码难以维护html
这还仅仅是柱状图,若是场景更加复杂呢?好比地图的交互,数据更加多维等等
git
本文将经过实验讲解d3的数据绑定是什么样的,了解数据绑定的三种状态update、exist、enter
小实验也将放在CodePen
之中方便你们修改测试github
d3经过data join(数据绑定)创造、更新及销毁元素,如何操做元素也能经过selection,个人总结以下:
编程
其中,selection的三种状态将data与element结合在一块儿,对元素进行控制
data、element、与三种状态之间的关系咱们来看看data join的概念图,图自d3做者Mike Bostock的文章Thinking with Joins
数组
接下来咱们用小实验进一步进行说明bash
咱们主要会用到如下API,这里根据官方原文档结合上图解释,更多细节请看官方文档app
Binds the specified array of data with the selected elements, returning a new selection that represents the update selection: the elements successfullypost
selection.data():绑定数据,返回表明
update的selection
,此selection表示成功绑定了数据的元素,update状态按上图理解表示又有数据又有元素;同时定义enter selection和exit selection
,能够用于添加或删除元素以匹配数量与数据一致
selection.enter():可在selection.data()返回
的selection中获取enter selection
selection.exit():可在selection.data()返回
的selection中获取exit selection
selection.join():添加、移除、重排元素至元素数量与绑定data的数量一致测试
<!-- div元素插入在app之下,修改样式做为柱图 -->
<div id="app">
<!-- 此处根据不一样实验肯定不一样的柱图(div)数量 -->
</div>
复制代码
#app div{
font: 10px sans-serif;
background-color: steelblue;
text-align: right;
padding: 3px;
margin: 1px;
color: white;
height:10px;
}
复制代码
//演示数据
const dataArray = [150, 250, 350];
复制代码
接下来是几个小实验,对应说明不一样API的功能
接下来开始咱们的实验✨
<!-- 柱状图元素插入在app之下 -->
<div id="app"></div>
复制代码
第一个例子咱们在控制台下详细看看selection.data()
的返回值,在后面的几个例子中也加以比对 先看例子效果以下:
CodePen打开
我也在CodePen中关键的地方console.log()
了,可是是第一个例子咱们就来逐行解释代码吧:
首先,选择#app元素,返回了selection对象
const app = d3.select("#app");
复制代码
const allSelection = app.selectAll("div") //欲选择#app下的div元素(即便本来#app下无div元素)
.data(dataArray) //进行数据绑定
console.log("————包含update、enter、exit三个状态的selection————");
console.log(allSelection);
复制代码
咱们看看对应的控制台,我用红色的箭头标出三种状态:
关于group与selection的关系可查看本人以前的总结或者Mike Bostock本人的文章),简单地说,selection是一个各元素为group的数组,每个group又是一个DOM数组。
接下来也会有对应的console打印selection.enter()及selection.exit()的代码,可在控制台查看
紧些着咱们获取enter状态的selection
const enterSelection = allSelection.enter();//获取enter状态的selection
console.log("————enter状态的selection————");
console.log(enterSelection);
复制代码
const divs = enterSelection.append("div")//在selection中插入"div"
console.log("——EnterNode被插入了div——");
console.log(divs);
//对每一个div设置宽度
divs.style("width", function (d) {
return d + "px";
});
复制代码
<!-- 柱状图元素插入在app之下 -->
<h1>app下子元素数量少于dataArray.length</h1>
<div id="app">
<div></div>
</div>
复制代码
CodePen打开
其实道理等同于#app下无子元素,都是没有足够的元素匹配数据
可是本来存在的div能到数据,即有一个元素是update状态
首先是进行数据绑定
const dataArray = [150, 250, 350];
const selection = d3.select('#app')
.selectAll('div')
.data(dataArray);
console.log('——————匹配到数据的元素——————')
console.log(selection);
//对匹配到的元素进行样式设置
selection.style('width', function (d) {
return d + 'px';
});
复制代码
发现selection中匹配了一个div元素
const enterSelection = selection.enter();
console.log('——————未匹配到数据,取得enter状态selection——————')
console.log(enterSelection);
console.log('——————插入div,设置样式——————')
enterSelection.append('div')
.style('width', function (d) {
return d + 'px';
});
复制代码
最后效果图与上文同样
<!-- 柱状图元素插入在app之下 -->
<div id="app">
<div></div>
<div></div>
<div></div>
</div>
复制代码
CodePen打开
此次咱们的app元素下对应刚恰好对应三个数据,元素数量与数据是匹配的。
const dataArray = [150, 250, 350];
const selection = d3.select('#app')
.selectAll('div')
.data(dataArray);
console.log('——————匹配到数据的元素——————')
console.log(selection);
selection.style('width', function (d) {
return d + 'px';
});
复制代码
咱们能够在data()返回的selection看到中匹配了三个div,正是对应update状态
<div id="app">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
复制代码
因为设置了高度,width默认为auto,因此初始下会占据整个屏幕
const dataArray = [150, 250, 350];
const selection = d3.select('#app')
.selectAll('div')
.data(dataArray);
console.log('——————匹配到数据的元素——————')
console.log(selection);
selection.style('width', function (d) {
return d + 'px';
});
复制代码
最后一个占满屏幕宽度的元素便是exit状态元素
const exitSelection = selection.exit();
exitSelection.remove();
复制代码
最后完成如上文的最终图
以前不管是对enter,exit以及update的操做可能都须要经过selection.enter()及selection.exit()等API获取selection,使用selection.join()能够极大地简化操做,同时局部渲染提升了效率。
这里咱们举两个简单的例子。
<div id="app"></div>
复制代码
<div id="app">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
复制代码
const dataArray = [150, 250, 350];
const selection = d3.select('#app')
.selectAll('div')
.data(dataArray).join('div')
.style('width', function (d) {
return d + 'px';
})
复制代码
以上仅仅只是静态数据,但咱们能够扩展到动态的数据,如data数组元素增长或减小,三种状态使得咱们便于操做数据,仅仅只需使用selection.join()或者selection.remove()等等
当数据大小改变,或数据量增多减小时,不须要使用if或者for等语法。update,enter及exit三种状态结合API使得语法简练,大幅度提高编程效率
其实意义同第一条很相像,三种状态能够方便咱们对进入图表或退出图表的元素建立动画,例子以下
<div id="app"></div>
复制代码
const data = [150, 250, 350];
const t = d3.transition()//定义动画变换
.duration(500)
.ease(d3.easeLinear);
let selection = d3.select('.chart')
.selectAll('div')
.data(data).join('div').style('width', 0).
transition(t) //使用动画变换
.style('width', d => d + 'px')
复制代码
好久之前刚开始学d3的时候一直不理解如此大费周章的意义,直到真正接触各种图表后才感慨此设计的妙处所在。
以上如有不足之处,还请指教交流