以 Join 的方式来思考D3.js

2018-06-13 admin

声明

原文链接: 来自 D3作者 Mike Bostock https://bost.ocks.org/mike/join/

译文地址: github repository

如果觉得还不错, 不妨去github给个star ?

Content

打个比方, 你想用D3画一个 散点图 , 用每一个svg的circle元素来可视化你的数据. 你会惊讶的发现: D3居然没有直接创建多个DOM元素的方法! 怎么回事?

当然, D3有 append 方法, 你可以用来创建单个元素. 比如:

svg.append("circle")
    .attr("cx", d.x)
    .attr("cy", d.y)
    .attr("r", 2.5);

但这只是一个圆, 如果你想要创建很多个圆(每一个圆代表一个数据点). 你可能会想到用一个for循环来实现 ? 这是非常直观的想法, 这个想法并没有什么错, 但是在这之前不妨看看D3中是如何实现创建多个元素的:

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, 还用这个方法的返回值去创建新的元素?

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

Join 模型

从上图中可以看到:

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

现在我们可以再来看看上面那段使用 enter-append 模型的代码了:

  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

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

原文链接:https://segmentfault.com/a/1190000015274831

本站文章除注明转载外,均为本站原创或编译。欢迎任何形式的转载,但请务必注明出处。

转载请注明:文章转载自 JavaScript中文网 [https://www.javascriptcn.com]

本文地址:https://www.javascriptcn.com/read-33939.html

文章标题:以 Join 的方式来思考D3.js

相关文章
一些前端学习中好的书籍,整理
一、Javascript方面的书籍: 1 JavaScript权威指南(第6版):号称javascript圣经,前端必备;前端程序员学习核心JavaScript语言和由Web浏览器定义的JavaScript API的指南和综合参考手册; 2...
2015-11-12
为什么要选择Nodejs?Nodejs有什么好处?
什么?JavaScript还能用作服务器编程! Caleb Madrigal是来自美国密尔沃基市的一名软件顾问。四年前,他在听说“将JavaScript用作服务器端语言”这样的说法时,认为那是一个荒唐的想法。有那么多服务器端语言可供选择,为...
2015-11-12
js性能优化 如何更快速加载你的JavaScript页面
确保代码尽量简洁 不要什么都依赖JavaScript。不要编写重复性的脚本。要把JavaScript当作糖果工具,只是起到美化作用。别给你的网站添加大量的JavaScript代码。只有必要的时候用一下。只有确实能改善用户体验的时候用一下。 ...
2015-11-12
10个强大的纯CSS3动画案例分享
我们的网页外观主要由CSS控制,编写CSS代码可以任意改变我们的网页布局以及网页内容的样式。CSS3的出现,更是可以让网页增添了不少动画元素,让我们的网页变得更加生动有趣,并且更易于交互。本文分享了10个非常炫酷的CSS3动画案例,希望大家...
2015-11-16
Node.js 2014这一年发生了什么
Node.js 的 2014 年充满了不幸和争议. 这一年 Noder 们经历了太多的伤心事, 经历了漫长的等待, 经历了沉重的分裂之痛. 也许 Noder 们不想回忆14年 Node.js land 发生的事情, 但正因为痛才更有铭记的价...
2015-11-12
Android中Okhttp3实现上传多张图片同时传递参数
之前上传图片都是直接将图片转化为io流传给服务器,没有用框架传图片。 最近做项目,打算换个方法上传图片。 Android发展到现在,Okhttp显得越来越重要,所以,这次我选择用Okhttp上传图片。 Okhttp目前已经更新到Okhttp...
2017-03-17
React.js编程思想
JavaScript框架层出不穷,在很多程序员看来,React.js是创建大型、快速的Web应用的最好方式。这一款由Facebook出品的JS框架,无论是在Facebook还是在Instagram中,它的表现都非常出色。 使用React.j...
2015-11-12
Angular2-primeNG文件上传模块FileUpload使用详解
近期在学习使用Angular2做小项目,期间用到很多primeNG的模块。 本系列将结合实战总结angular2-primeNG各个模块的使用经验。 文件上传模块FileUploadModule 首先要在使用该组件的模块内导入文件上传模块 ...
2017-03-09
从2014年的发展来展望JS的未来将会如何
<font face="寰�杞�闆呴粦, Arial, sans-serif ">2014骞达紝杞�浠惰�屼笟鍙戝睍杩呴€燂紝鍚勭�嶈��瑷€灞傚嚭涓嶇┓锛屼互婊¤冻鐢ㄦ埛涓嶆柇鍙樺寲鐨勯渶姹傘€傝繖浜涜��...
2015-11-12
12个你未必知道的CSS小知识
虽然CSS并不是一种很复杂的技术,但就算你是一个使用CSS多年的高手,仍然会有很多CSS用法/属性/属性值你从来没使用过,甚至从来没听说过。 1.CSS的color属性并非只能用于文本显示 对于CSS的color属性,相信所有Web开发人员...
2015-11-12
回到顶部