原文连接: 来自 D3做者 Mike Bostock bost.ocks.org/mike/join/javascript
译文地址: github repositoryjava
若是以为还不错, 不妨去github给个star ?git
打个比方, 你想用D3画一个 散点图
, 用每个svg的circle元素来可视化你的数据. 你会惊讶的发现: D3竟然没有直接建立多个DOM元素的方法! 怎么回事?github
固然, D3有 append
方法, 你能够用来建立单个元素. 好比:app
svg.append("circle")
.attr("cx", d.x)
.attr("cy", d.y)
.attr("r", 2.5);
复制代码
但这只是一个圆, 若是你想要建立不少个圆(每个圆表明一个数据点). 你可能会想到用一个for循环来实现 ? 这是很是直观的想法, 这个想法并无什么错, 可是在这以前不妨看看D3中是如何实现建立多个元素的:svg
svg.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", 2.5);
复制代码
上面这段代码完美的实现了你想要的效果: 为每个数据点建立了一个 circle, 用数据点的 x
和 y
属性做为circle的坐标. 但这段代码里面的 selectAll("circle")
是什么意思? 咱们为何要 select
咱们知道当前并不存在的 circle, 还用这个方法的返回值去建立新的元素?post
这段代码的思想是: 不要告诉D3如何去作, 而是告诉D3你想要的效果. 你想要circle元素和数据一一对应, 那么你就不该该告诉D3去建立circle元素, 而是告诉D3: .selectAll("circle")
获得的circle集合应该和 .data(data)
一一对应. 这个思想就叫作 Join.动画
从上图中能够看到:spa
数据集合
和 DOM元素集合
相交产生了中间的 update
集合enter
集合 (也就是缺失DOM元素)exit
集合 (也就意味着这些DOM元素将被移除)如今咱们能够再来看看上面那段使用 enter-append
模型的代码了:3d
svg.selectAll("circle")
返回的是一个空的集合, 由于当前 svg 容器仍是空的. 这里的 svg 是全部后续建立的 circle元素的父节点.svg.selectAll("circle")
返回的集合接下来和 data
进行 Join 操做, 获得的就是咱们上面提到的三个集合: update
集合 , enter
集合 , exit
集合. 由于初始时 Elements集合(也就是circle集合)是空的, 因此 update
和 exit
集合为空, 而 enter
集合会自动为每个新的data元素生成一个占位符..data(data)
返回的是 update
集合, 由于 update
集合为空, 因此咱们不对其进行操做, 这里咱们调用 .enter()
获得 enter
集合.enter
集合中的每个元素, 咱们使用 selection.append('circle')
(值得注意的是, 对集合的操做会被应用到集合中的每个元素上去). 这样就为每个数据点建立了一个 circle
(这些circle都在他们的父节点 svg 中)用 Join 的方式来思考意味着, 咱们要作的事情仅仅是声明 DOM集合(好比这里的 circle 集合) 和数据集合之间的关系, 而且经过处理三个不一样状态的集合 enter, update , exit 来描述这种关系.
你也许会问, 为何要用这种方式来进行个人数据可视化工做呢? 好处在哪? 为何我不直接用for循环建立全部我想要的元素? 答案是这个思想确实是很是有好处的, 它的优美之处在于它的归纳性. 如今咱们的代码还只是处理了 enter
的部分, 这部分对于展现静态的数据已经足够了, 但若是你想进行动态的数据展现, 这种 Join 的方式将大大简化你的工做, 你只须要对 update
和 exit
进行不多的操做就能获得你想要的效果. 这也意味着你能够轻松的展现实时数据, 可以为用户添加动态的交互, 能平滑的切换不一样的展现数据集.
下面这段代码展现了对于 exit
和 update
集合的处理:
var circle = svg.selectAll("circle")
.data(data);
circle.exit().remove();
circle.enter().append("circle")
.attr("r", 2.5)
.merge(circle)
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
复制代码
不管何时上面的这段代码被执行, 它都将从新计算 Join 而且维护好 DOM元素集合 和 数据集合 之间的对应关系. 若是你的新数据集比以前老的数据集要小, 多余的DOM元素就会进入 exit
集合, 而后被 remove掉. 若是新的数据集比老的大, 那么新的数据就将进入 enter
集合, 并建立出新的DOM元素. 若是新的数据集和老的数目相同, 那么只有 update
集合会被更新坐标.
使用 Join 的思想能让咱们的代码更加直观. 你只须要处理好这三种状态的集合, 而不须要 if
和 for
来进行复杂的逻辑判断. 你只须要描述好你的数据集合和DOM集合想要有怎样的对应关系.
Join 还让你能够对不一样状态的DOM元素进行不一样的操做. 好比, 你能够只对 enter 集合进行操做, 这样就不会每次都对全部的 DOM元素进行更新, 这能显著的提高你的数据可视化做品的渲染效率. 一样的, 你也能够给指定集合的元素添加动画效果, 好比给 enter 的元素添加放大进入的效果:
circle.enter().append("circle")
.attr("r", 0)
.transition()
.attr("r", 2.5);
复制代码
或者给 exit
的集合添加 缩小隐藏 的效果:
circle.exit().transition()
.attr("r", 0)
.remove();
复制代码
这里有一个很是好的实践 Join 思想的例子(一样来自D3做者), 不妨看看:
这里是个人 D3.js 、 数据可视化 的github 地址, 欢迎 start & fork :tada: