前文中已经提到,SVG从诞生之初起就可以非常方便地使用javascript脚本语言来进行其DOM对象的控制。当然,控制的方法有很多,有直接控制SVG对象的方法,例如使用原生js;有帮你封装一下图形接口再进行直接控制的js类库,如 Raphaël。但是正如我在第一篇文章中所说,d3作为一个中间型类库还能脱颖而出的重要原因,在于它突破了其他类库的那种直接控制表现层的机制,而采用了对于web图形处理领域较为新颖的数据驱动机制(2011),并获得了极大的成功。

数据驱动的历史

数据驱动编程并不是一个新鲜玩意,在老书《Unix编程艺术》中,作者在介绍Unix设计原则时,其中有一条为“表示原则:把知识叠入数据以求逻辑质朴而健壮”。其核心出发点是相对于程序逻辑,人类更擅长于处理数据。数据比程序逻辑更容易驾驭,所以我们应该尽可能的将设计的复杂度从程序代码转移至数据。这条原则早在70年代的软件危机中就得到了印证:凡是执着于逻辑实现的代码最终都难以维护,而将逻辑变化沉淀到数据集中的程序,则撑过了时间的检验。在web开发领域流行20多年的MVC架构,核心也是将数据、表现、操作分离从而降低程序复杂度。JAVA社区多年来就程序设计领域的讨论,翻来覆去也离不开数据驱动这个原则。

然而,web前端,尤其是图形图像处理领域,长期以来人们的关注点集中在图形渲染API,设计诸如var circle = paper.circle(50, 40 ,10)这样的图形呈现接口。对图形元素的使用,也长期停留在使用程序直接控制的状态。当然,如果交互逻辑少,即使图形略微复杂,这么做并非不可以。但是在创造交互逻辑复杂的数据可视化项目时,这种做法的代码复杂度会随着交互逻辑的规模指数上升,并带来许多意料之外的程序冲突——这一切与MVC之前的web开发领域或者70年代的软件危机原因如出一辙。有识之士很早就意料到这种问题,于是在web端图形处理的基础渲染技术已经渐渐完善的2011年,D3js诞生,并迅速在数据可视化领域和web图形渲染领域掀起了旋风。

对D3数据转换的理解

官网上的原文说,它注重的是是转化而不是表现(Transformation, not Representation)。d3不是一个新的图形呈现类库。不像 ProcessingRaphaël, 或者 Protovis这类类库,D3不注重对图形呈现接口的封装,而是使用web标准的HTML, SVG和CSS来处理表现层。这意味着如果哪一天浏览器支持了新的图形渲染特性,那么你就可以立即用上,而不需要在代码层有什么特殊的修改。D3将重点放在了数据与图形之间的绑定上。数据一般是json格式的对象,也可以是xml;而图形不仅包括SVG对象,也可以是html对象,或者其他浏览器所支持的图形对象。总之,同一组数据,你可以使用D3来创建一个HTML table,也可以创建一个SVG柱状图。

那么,D3是如何将数据与DOM节点之间绑定的呢?

第一步,是要选中DOM对象以构建选择区(selection)。 选中DOM对象的方式跟Jquery如出一辙,都是使用类似CSS选择器的语法,例如选中一组css类名为my-node的div节点并使之文字为白色:

 
1
d3.selectAll("div.my-node").style("color", "white");

第二步,就是将数据集绑定到选择区(selection)的DOM对象上。D3的提供一个十分有用的data函数,用以进行数据对象序列化和可能的预处理工作(详见API),并且它会把集合元素一个一个返回用以进行下一步处理。例如下面这个例子:

 
1
2
3
d3.selectAll("p")
    .data([4, 8, 15, 16, 23, 42])
    .style("font-size", function(d) { return d + "px"; });

在这个例子中,选择区(selection)中的每个P标签都与数据中的一个数字按顺序一一对应地绑定起来(我们假设P标签的个数与数组长度相等)。这样最后回调函数中,就可以取回被绑定的数据,从而动态地计算每个P标签中文字大小。熟悉jquery或者prototype类库的程序员都能很容易地发现他们之间的共相似之处,尤其是通过回调函数的方式来实现对一个集合中每个元素进行操作。

上面这个例子看起来很简单很容易理解。但是其中影藏一个重大的问题,就是数据集是经常变化的,一旦变化,那么数据集中的每个数据对象和选择区(selection)中的DOM对象不再一一对应了,那么多余的数据或者多余的dom对象该怎么处理呢?

为此,D3专门提供了一个enter, update, exit三个行为的数据集合(data)与选择区(selection)绑定的机制。要理解这个机制,最好还是得看图看例子,要不然不容易理解。这有一个d3的简单教程可以下载。下面这个图就是从该教程中摘出的D3数据绑定机制示意图,其解释如下:

  1. 数据集(data)和选择区(selection)中的对象应该是一一对应地绑定的。绑定的数据,一旦发生变化都会反映给对应的dom对象,这叫做update过程。
  2. 如果数据数量大于节点的数量,那么有一部分数据放不下了,将产生enter过程。
  3. 反之,如果数据从节点中取出来,导致节点空闲,这就发生exit的过程。

以上update, enter, exit三个过程,你都可以进行一系列自己特别定制。例如,通过ajax获取数据,初始化图形时,你可以通过定制enter过程来实现渐变地显示图形;在用户点击一个图形对象时,通过改变该图形对象所对应的数据数值,然后通过update过程来同时修改图形对象;当要删除某个图形对象时,触发exit过程,来实现渐隐的效果。于是,我们看到github上大量d3例子的代码,都类似如下分成三大块:update, enter, exit…

 
1
2
3
4
5
6
7
8
9
10
11
// Update…
var p = d3.select("body").selectAll("p")
    .data([4, 8, 15, 16, 23, 42])
    .text(String);
 
// Enter…
p.enter().append("p")
    .text(String);
 
// Exit…
p.exit().remove();

多说无用,我们还是来看具体的代码吧。我根据上文提到的教程中的案例创建了一个在线的例子:http://runjs.cn/detail/8enw0npq,在这里大家可以直接看到代码,我这里就不赘述了。大家可以看看d3的数据绑定机制究竟如何,enter, update, exit过程都是怎么工作的。

<iframe style=”width: 100%; height: 300px” src=”http://sandbox.runjs.cn/show/8enw0npq” allowfullscreen=”allowfullscreen” frameborder=”0″></iframe>

反思学习历程

就我的面试经验,国内的前端程序员大多数都是从jquery开始学javascript的。那么想必看完上面的代码,大家都会觉得跟jquery非常像,链式编程的风格,回调函数,很容易理解。

但为什么还说D3的学习曲线比较高呢?这是因为一般只看完一两个github上的D3 demo,没有专门的讲解,普通程序员还是难以彻底理解D3的那种以数据(以及容纳数据的容器)为核心的思路。至少我当初就没一下子搞明白为什么要搞update, enter, exit这么三个过程。这是因为常规的思维,对于图形操作,首先都把注意力集中在图形本身上。例如,一个通常的jquery程序员,遇到一个需求:在点击一个图形后将图形变大一倍同时把数据更新为原来的2倍。他通常都是这么构思整个流程的:先选中图形对象,改变图形,然后更新数据(或者先更新数据再改变图形)。虽然这样想逻辑很简单,但这样做无疑将数据和图形孤立了起来。如果这个图形还绑定了很多别的交互,数据集又很复杂,那么就会经常遇到顾头不顾腚的维护难题。D3处理交互逻辑时,通常是先通过图形对象找到数据,之后改变数据,最后再update更新图形对象。似乎转了一个圈才最后返回到图形本身,比起直接操作图形要麻烦。但是这里才是D3的精华所在。只有把复杂逻辑沉淀到数据上,才能在复杂交互的情况下保持代码的清晰性。

当然,如果你只是为了画几个图,并没有太多复杂交互,我认为D3就没多少优势了。有很多用jquery的chart插件实现的简单图表,并不比D3实现类似的案例多多少代码量,也能收到很好的效果。所以我以为D3的主要用武之地还是在进行较为复杂的交互式可视化项目开发时。而且,一旦理解了D3的数据驱动模式,思路就会广阔许多。将它与backbone,angularjs,ember.js等框架一做比较,确实地感到,所谓前端开发革命的浪潮,就是从数据驱动开始。

d3可视化实战02:理解d3数据驱动的真正含义的更多相关文章

  1. d3可视化实战00:d3的使用心得和学习资料汇总

    最近以来,我使用d3进行我的可视化工具的开发已经3个月了,同时也兼用其他一些图表类库,自我感觉稍微有点心得.之前我也写过相关文章,我涉及的数据可视化的实现技术和工具,但是那篇文章对于项目开发而言太浅了 ...

  2. d3可视化实战03:神奇的superformula

    需求驱动实现 前文讲过了D3的数据驱动机制,中间所举的例子都很简单.例如那个demo里面,绑定的数据是一个简单的数组,实现的图元也仅仅是一堆用SVG画的circle.但是现实世界中我们往往会遇到复杂的 ...

  3. d3可视化实战01:理解SVG元素特性

    一. SVG简介 ————————————————————————————————————————————————————————————————— SVG是一种和图像分辨率无关的矢量图形格式,它使用 ...

  4. d3可视化实战04:事件绑定机制

    首先说明,d3支持所有的JS事件——甚至其他代码的自定义事件.这里有一个列表,The MDN Event Reference, 包含了几乎所有浏览器创建的事件类型.大家有需要可以去查看. D3的事件绑 ...

  5. D3 数据可视化实战 笔记

    学习真是件奇妙的事情.这本书我之前都看过,有些的知识点却完全没有印象. 总结:把用到的知识好好研究:平时可以了解其他技术的基础,把相关的资料和难点记录下来. javascript陷阱 1.变量类型 v ...

  6. Pentaho6.1中D3可视化库的集成及数据联动的实现

    1.软件环境 操作系统版本:Win 10 64位 可视化图形库:D3 Pentaho版本: biserver-ce-6.1.0.1-196 2.对D3的简单介绍 D3允许你将任意的数据绑定到文档对象模 ...

  7. D3--数据可视化实战总结

    d3理解 标签(空格分隔): 未分类 1.绑定数据 [x] 定义:通过循环的方式将数据绑定在dom元素上,每个数据对应一个元素,所以这个数据的值就能来设定dom元素的width,height,x,y坐 ...

  8. [D3] 12. Basic Transitions with D3

    <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...

  9. [D3] 10. Creating Axes with D3

    <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...

随机推荐

  1. UVALive 4957 Fake scoreboard

    题意就是有n个队伍和m个题目 给出了每个队伍解决的题目数量 每个题目也给出了被解决的次数 然后求一个方阵. N,Y表示每个队伍是否过了哪个题目. 要求字典序最小. 这题给人的第一反应就是网络流. 虽然 ...

  2. HTTP学习实验8-windows添加telnet功能

    Windows 添加telnet功能: 控制面板->(查看方式:小图标)->程序和功能->打开或关闭Windows功能->Telnet客户端 Telnet 设置: 打开cmd, ...

  3. 修改CKFinder上传路径

    一.CKFinder的若干问题 1.单独使用 ckfinder从原fckeditor分离出来以后可以单独使用,通常我习惯于在工具栏中添加ckfinder.dll,这样以后要使用ckfinder直接从工 ...

  4. lesson4:使用锁Lock来解决重复下单的问题

    demo源码:https://github.com/mantuliu/javaAdvance 中的类Lesson4IndependentLock 在上一节中,我们分析了Lock的源代码并一起实践了粗粒 ...

  5. jQuery实现密保互斥问题

    密保互斥问题: 密保通常都会有n个问题,让用户选择其中2.3个,而且都不会让用户选择重复的问题.这就要求密保互斥. 效果如下: 下面我用了jquery实现密保互斥,用于解决密保,投票等类似互斥问题,可 ...

  6. swift 深入理解Swift的闭包

    我们可用swift的闭包来定义变量的值. 先来一个简单的例子大家先感受感受. 定义一个字符串的变量的方法: 直接赋值 var str="JobDeer" 还可以用闭包的方式定义: ...

  7. Linux磁盘分区实战案例

    一.查看新添加磁盘   [root@localhost /]# fdisk -l   磁盘 /dev/sda:53.7 GB, 53687091200 字节,104857600 个扇区 Units = ...

  8. 安装android studio 出现的路径问题 tools.jar' seems to be not in Android Studio classpath

    尝试一下android studio  ,谁知出现路径问题 'tools.jar' seems to be not in Android Studio classpath. Please ensure ...

  9. exploit writing tutorial 阅读笔记总结

    近日阅读Corelan Team编写的exploit writing tutorial系列,大致了解了一下原理,记了一些笔记.此系列文章有中文翻译版,在看雪论坛上发表. 英文版地址:https://w ...

  10. #ifndef #define #endif 的用法

    1.文件中的#ifndef 头件的中的#ifndef,这是一个很关键的东西.比如你有两个C文件,这两个C文件都include了同一个头文件.而编译时,这两个C文件要一同编译成一个可运行文件,于是问题来 ...