d3.js数据绑定及示例详解

前言

数据绑定(data join)做为d3的一大特色,极大方便了对数据及元素的操做

哪里方便呢?这里列举一个简单的例子:
假设需求以下:有数据[5, 15, 25, 35],制做一张柱状图,同时添加交互,鼠标移至对应柱子时变色css

若是咱们有数据绑定,咱们将数据绑定到元素,返回selection以后,有如交互、动画、更新等的操做直接经过selection进行便可

若是没有数据绑定,单纯的使用js与div元素生成柱状图,在交互添加,监听更新方面都得使用for循环来添加等等,不但开发效率低,并且代码难以维护html

这还仅仅是柱状图,若是场景更加复杂呢?好比地图的交互,数据更加多维等等

git

本文将经过实验讲解d3的数据绑定是什么样的,了解数据绑定的三种状态updateexistenter
小实验也将放在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的数量一致测试


以上是我的根据文档的粗略翻译,更官方细致的解释推荐看官方文档 解释错误或不到位还请大佬指出
接下来咱们以几个实验来讲明这些API在数据绑定上的做用 先列出基本的html结构、css及示例数据
<!-- 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下无子元素:可经过enter()添加元素至数量与dataArray.length一致
  • #app下子元素数量少于dataArray.length:可经过enter()添加元素至数量与dataArray.length一致
  • #app下子元素数量等于dataArray.length:selection.data()返回的selection直接操控
  • #app下子元素数量多于dataArray.length:可经过exit()操控多余元素
  • join简化操做

接下来开始咱们的实验✨

#app下无子元素

<!-- 柱状图元素插入在app之下 -->
<div id="app"></div>
复制代码

第一个例子咱们在控制台下详细看看selection.data()的返回值,在后面的几个例子中也加以比对 先看例子效果以下:

CodePen打开
我也在CodePen中关键的地方console.log()了,可是是第一个例子咱们就来逐行解释代码吧:

首先,选择#app元素,返回了selection对象

const app = d3.select("#app");
复制代码

接着,咱们想选择#app下的div元素进行数据绑定,即便这时初始的#app下没有子元素
const allSelection = app.selectAll("div") //欲选择#app下的div元素(即便本来#app下无div元素)
                        .data(dataArray) //进行数据绑定
console.log("————包含update、enter、exit三个状态的selection————");
console.log(allSelection);
复制代码

咱们看看对应的控制台,我用红色的箭头标出三种状态:


其中_groups是对应selection.data()所返回表明update状态元素,同时定义的enter状态元素对应_enter,exit状态元素对应_exit
selection.enter()、selection.exit()也是将返回的_groups做为各自对应的状态元素。

关于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);
复制代码



最后咱们在EnterNode中插入div,根据绑定的数据设置样式(这里是宽度)

const divs = enterSelection.append("div")//在selection中插入"div"
    console.log("——EnterNode被插入了div——");
    console.log(divs);
    
    //对每一个div设置宽度
    divs.style("width", function (d) {
       return d + "px";
    });
复制代码


最终绘制出图形

#app下子元素数量少于dataArray.length

<!-- 柱状图元素插入在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元素


代码中对他进行了样式设置,你们能够去掉那部分代码看看会发生什么

接着获取enterNode,插入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下子元素数量等于dataArray.length

<!-- 柱状图元素插入在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状态


接着对应上面代码进行样式设置便可

#app下子元素数量多于dataArray.length

<div id="app">
            <div></div>
            <div></div>
            <div></div>
            <div></div>
        </div>
复制代码

因为设置了高度,width默认为auto,因此初始下会占据整个屏幕


咱们先对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';
});
复制代码

最后一个占满屏幕宽度的元素便是exit状态元素


咱们获取exit元素并将他去除:

const exitSelection = selection.exit();
exitSelection.remove();
复制代码

最后完成如上文的最终图

join简化操做

以前不管是对enter,exit以及update的操做可能都须要经过selection.enter()及selection.exit()等API获取selection,使用selection.join()能够极大地简化操做,同时局部渲染提升了效率。
这里咱们举两个简单的例子。

子元素少于数据项或无子元素

<div id="app"></div>
复制代码

CodePen打开

子元素多于数据项

<div id="app">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>
复制代码

CodePen打开


以上两个例子的js代码都是同样的
const dataArray = [150, 250, 350];

const selection = d3.select('#app')
    .selectAll('div')
    .data(dataArray).join('div')
    .style('width', function (d) {
        return d + 'px';
    })
复制代码

data join思想的意义

1.有利于动态数据的可视化编程

以上仅仅只是静态数据,但咱们能够扩展到动态的数据,如data数组元素增长或减小,三种状态使得咱们便于操做数据,仅仅只需使用selection.join()或者selection.remove()等等

2.编程更偏向声明式

当数据大小改变,或数据量增多减小时,不须要使用if或者for等语法。update,enter及exit三种状态结合API使得语法简练,大幅度提高编程效率

3.方便添加动画效果

其实意义同第一条很相像,三种状态能够方便咱们对进入图表或退出图表的元素建立动画,例子以下

<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的时候一直不理解如此大费周章的意义,直到真正接触各种图表后才感慨此设计的妙处所在。

以上如有不足之处,还请指教交流

参考资料

Thinking with Joins
d3文档——Joining Data

相关文章
相关标签/搜索