以 Join 的方式来思考D3.js
声明
原文链接: 来自 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, 用数据点的 x
和 y
属性作为circle的坐标. 但这段代码里面的 selectAll("circle")
是什么意思? 我们为什么要 select
我们知道当前并不存在的 circle, 还用这个方法的返回值去创建新的元素?
这段代码的思想是: 不要告诉D3如何去做, 而是告诉D3你想要的效果. 你想要circle元素和数据一一对应, 那么你就不应该告诉D3去创建circle元素, 而是告诉D3: .selectAll("circle")
得到的circle集合应该和 .data(data)
一一对应. 这个思想就叫做 Join.
从上图中可以看到:
-
数据集合
和DOM元素集合
相交产生了中间的update
集合 - 没有DOM元素与之对应的Data产生了左边的
enter
集合 (也就是缺失DOM元素) - 同样的, 所有没有数据与之对应的DOM元素产生了右边的
exit
集合 (也就意味着这些DOM元素将被移除)
现在我们可以再来看看上面那段使用 enter-append
模型的代码了:
- 首先,
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 ?
这里是我的 D3.js 、 数据可视化 的github 地址, 欢迎 start & fork :tada:
如果觉得不错的话, 不妨点击下面的链接关注一下 : )

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
[译] D3.js 嵌套选择集 (Nested Selection)
译者注: 原文: Mike Bostock (D3.js 作者) -- Nested Selections 译者: ssthouse 本文讲解的是关于 D3.js 中 d3-selection 的使用. d3-selection 是 d3 的核心所在, 它提供了一种和以往 Dom 操作 和 数据操作 完全不同的思路, 让我们能非常优雅的进行数据可视化工作.本文是 d3 作者对于 d3-selection 中 嵌套选择集 的讲解, 本人阅读后觉得很有启发, 所以翻译成中文, 希望对读者也能有所帮助. 译文 D3 的 选择集是分层级的, 就像 Dom 元素和数据集是可以有层级的一样. 比如说 Table: <table> <thead> <tr><td> A</td><td> B</td><td> C</td><td> D</td></tr> </thead> <tbody> <tr><td> 0&l...
- 下一篇
使用 github pages, 快速部署你的静态网页
使用 github pages, 快速部署你的静态网页 Github Pages 官网 Github Pages:Websites for you and your projects.Hosted directly from your GitHub repository. Just edit, push, and your changes are live. 前言 在日常工作中, 我们经常会遇到要做 demo 展示的情况. 做 demo 展示不同于做项目开发, 我们需要的是快速轻便的开发和部署, 而不是完备的一整套开发流程. 下图肯定不是我们做一个 demo 想要的流程. 尤其对于数据可视化工作, 能快速的创建一个 demo 来验证自己的想法, 并且方便的和同伴分享自己作品是非常重要的.在这里给大家介绍一种笔者经常用来做 demo 的方法: Github Pages. 选择 github pages 的理由 使用零成本: github pages 集成在 github 中, 直接和代码管理绑定在一起, 随着代码更新自动重新部署, 使用非常方便. 免费: 免费提供 username.gi...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7设置SWAP分区,小内存服务器的救世主
- Hadoop3单机部署,实现最简伪集群
- CentOS8安装Docker,最新的服务器搭配容器使用
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- Docker快速安装Oracle11G,搭建oracle11g学习环境