以 Join 的方式来思考D3.js

声明

原文连接: 来自 D3做者 Mike Bostock bost.ocks.org/mike/join/javascript

译文地址: github repositoryjava

若是以为还不错, 不妨去github给个star ?git

Content

打个比方, 你想用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, 用数据点的 xy 属性做为circle的坐标. 但这段代码里面的 selectAll("circle") 是什么意思? 咱们为何要 select 咱们知道当前并不存在的 circle, 还用这个方法的返回值去建立新的元素?post

这段代码的思想是: 不要告诉D3如何去作, 而是告诉D3你想要的效果. 你想要circle元素和数据一一对应, 那么你就不该该告诉D3去建立circle元素, 而是告诉D3: .selectAll("circle") 获得的circle集合应该和 .data(data) 一一对应. 这个思想就叫作 Join.动画

Join 模型

从上图中能够看到:spa

  • 数据集合DOM元素集合 相交产生了中间的 update 集合
  • 没有DOM元素与之对应的Data产生了左边的 enter 集合 (也就是缺失DOM元素)
  • 一样的, 全部没有数据与之对应的DOM元素产生了右边的 exit 集合 (也就意味着这些DOM元素将被移除)

如今咱们能够再来看看上面那段使用 enter-append 模型的代码了:3d

  1. 首先, svg.selectAll("circle") 返回的是一个空的集合, 由于当前 svg 容器仍是空的. 这里的 svg 是全部后续建立的 circle元素的父节点.
  2. svg.selectAll("circle") 返回的集合接下来和 data 进行 Join 操做, 获得的就是咱们上面提到的三个集合: update 集合 , enter 集合 , exit 集合. 由于初始时 Elements集合(也就是circle集合)是空的, 因此 updateexit 集合为空, 而 enter 集合会自动为每个新的data元素生成一个占位符.
  3. 默认 .data(data) 返回的是 update 集合, 由于 update 集合为空, 因此咱们不对其进行操做, 这里咱们调用 .enter() 获得 enter 集合.
  4. 接下来, 对于 enter 集合中的每个元素, 咱们使用 selection.append('circle') (值得注意的是, 对集合的操做会被应用到集合中的每个元素上去). 这样就为每个数据点建立了一个 circle (这些circle都在他们的父节点 svg 中)

Join 的方式来思考意味着, 咱们要作的事情仅仅是声明 DOM集合(好比这里的 circle 集合) 和数据集合之间的关系, 而且经过处理三个不一样状态的集合 enter, update , exit 来描述这种关系.

你也许会问, 为何要用这种方式来进行个人数据可视化工做呢? 好处在哪? 为何我不直接用for循环建立全部我想要的元素? 答案是这个思想确实是很是有好处的, 它的优美之处在于它的归纳性. 如今咱们的代码还只是处理了 enter 的部分, 这部分对于展现静态的数据已经足够了, 但若是你想进行动态的数据展现, 这种 Join 的方式将大大简化你的工做, 你只须要对 updateexit 进行不多的操做就能获得你想要的效果. 这也意味着你能够轻松的展现实时数据, 可以为用户添加动态的交互, 能平滑的切换不一样的展现数据集.

下面这段代码展现了对于 exitupdate 集合的处理:

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 的思想能让咱们的代码更加直观. 你只须要处理好这三种状态的集合, 而不须要 iffor 来进行复杂的逻辑判断. 你只须要描述好你的数据集合和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做者), 不妨看看:

Mike Bostock 的实现

join example

这里是我对这个例子的实现(也包括一些其余的案例):

想继续了解 D3.js ?

这里是个人 D3.js数据可视化 的github 地址, 欢迎 start & fork :tada:

D3-blog

若是以为不错的话, 不妨点击下面的连接关注一下 : )

github主页

知乎专栏

掘金

欢迎关注个人公众号:

相关文章
相关标签/搜索