最近项目组加班比较严重,D3的博客就一拖再拖,今天终于不用加班了,赶紧抽点时间写完~~

今天就将D3数据的更新及动画写一写~~

接着之前的博客写~~

之前写了一个散点图的例子,下面可以自己写一个柱状图的例子。

我就直接给代码了,和散点图差不多~~

  1. var margin = {top: 20, right: 20, bottom: 30, left: 40},
  2. width = 960 - margin.left - margin.right,
  3. height = 500 - margin.top - margin.bottom;
  4. var dataset = [ 11, 12, 15, 20, 18, 17, 16, 18, 23, 25, 8, 10, 13, 19, 21, 25, 22, 18, 15, 13];
  5. // 使用了d3.scale.ordinal() 它支持范围分档。与定量比例尺(如d3.scale.linear())返回连续的范围值不同,序数比例尺使用的是离散范围值,也就是输出值是事先就确定好的。
  6. // 映射范围时,可以使用range(),也可以使用rangeBands()。后者接收一个最小值和一个最大值,然后根据输入值域的长度自动将其切分成相等的块或“档”。0.2也就是档间距为每一档宽度的20%。
  7. var x = d3.scale.ordinal()
  8. .domain(d3.range(dataset.length))
  9. .rangeBands([0, width], 0.2);
  10.  
  11. var y = d3.scale.linear()
  12. .domain([0, d3.max(dataset, function(d) { return d; })])
  13. .range([height, 0]);
  14.  
  15. var xAxis = d3.svg.axis()
  16. .scale(x)
  17. .orient("bottom");
  18.  
  19. var yAxis = d3.svg.axis()
  20. .scale(y)
  21. .orient("left");
  22.  
  23. var svg = d3.select("body").append("svg")
  24. .attr("width", width + margin.left + margin.right)
  25. .attr("height", height + margin.top + margin.bottom)
  26. .append("g")
  27. .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
  28.  
  29. svg.selectAll("rect")// 插入的不是circle了,改为rect
  30. .data(dataset)
  31. .enter()
  32. .append("rect")
  33. .attr("x", function(d,i) {
  34. return x(i);
  35. })
  36. .attr("y", function(d) {
  37. return y(d);
  38. })
  39. .attr("width", x.rangeBand())
  40. .attr("height", function(d) {
  41. return height - y(d);
  42. })
  43. .attr("fill", function(d){
  44. return "rgb(60, 127, " + d * 10 + ")";// 根据值的大小获取颜色
  45. });
  46. svg.append("g")
  47. .attr("class", "x axis")
  48. .attr("transform", "translate(0," + height + ")")
  49. .call(xAxis)
  50. .append("text")
  51. .attr("class", "label")
  52. .attr("x", width)
  53. .attr("y", -6)
  54. .style("text-anchor", "end")
  55. .text("X轴");
  56.  
  57. svg.append("g")
  58. .attr("class", "y axis")
  59. .call(yAxis)
  60. .append("text")
  61. .attr("class", "label")
  62. .attr("transform", "rotate(-90)")
  63. .attr("y", 6)
  64. .attr("dy", ".71em")
  65. .style("text-anchor", "end")
  66. .text("Y轴");

其效果如下

坐标轴有些粗,是因为CSS没设置,如果设置上在之前博客里的CSS样式,会好看一点~~

先添加一个事件来触发数据的变化,在html中body标签里添加一个button

  1. <button>Update</button>

为button绑定事件,并在事件中添加数据的更新,代码如下

  1. // 单击的时候,更新数据
  2. d3.select("button").on("click", function() {
  3. // 新数据集
  4. dataset = [ 21, 22, 25, 10, 18, 17, 6, 8, 13, 15, 15, 20, 23, 19, 11,15, 25, 8, 25, 23 ];
  5. // 更新所有矩形
  6. svg.selectAll("rect")
  7. .data(dataset)
  8. .attr("y", function(d) {
  9. return y(d);
  10. })
  11. .attr("height", function(d) {
  12. return height - y(d);
  13. });
  14. });

运行代码,点击update按钮,chart发生变化,如下

如果你足够细心的话,就会发现,颜色跟原来的一样,没有根据长度发生变化,只要把原来针对fill 编写的代码复制到事件的代码里。

  1. svg.selectAll("rect")
  2. .data(dataset)
  3. .attr("y", function(d) {
  4. return y(d);
  5. })
  6. .attr("height", function(d) {
  7. return height - y(d);
  8. })
  9. .attr("fill", function(d){
  10. return "rgb(60, 127, " + d * 10 + ")";// 根据值的大小获取颜色
  11. });

是不是颜色也跟着变化了。

你是不是觉得,就这么变化有些太坑了,那我们就给它加个动画,过渡一下就OK了,其实现只需简单的一行代码 .transition()

注:在方法链上,要把这个调用插到选择元素之后,改变任何属性之前

  1. svg.selectAll("rect")
  2. .data(dataset)
  3. .transition() // <-- 这是新代码,其他都保持不变。
  4. .attr("y", function(d) {
  5. return y(d);
  6. })
  7. .attr("height", function(d) {
  8. return height - y(d);
  9. })
  10. .attr("fill", function(d){
  11. return "rgb(60, 127, " + d * 10 + ")";// 根据值的大小获取颜色
  12. });

是不是有了动画,就不会觉得变化有些突然了~~

之后,我觉得想控制一下动画的时间,要让他快一点或者慢一点,其实现也只需要一行代码 .duration(2000)

  1. svg.selectAll("rect")
  2. .data(dataset)
  3. .transition()
  4. .duration(2000) // <-- 这是新代码,其他都保持不变。2000 毫秒,即2 秒
  5. .attr("y", function(d) {
  6. return y(d);
  7. })
  8. .attr("height", function(d) {
  9. return height - y(d);
  10. })
  11. .attr("fill", function(d){
  12. return "rgb(60, 127, " + d * 10 + ")";// 根据值的大小获取颜色
  13. });

你延长了一下动画时间,发现它的动画一开始非常慢,然后逐渐加速,最后在达到预定高度之前速度再次慢了下来。换句话说,动画的速度不是线性不变的,而是有加减速变化的。

如果,我想要均匀的变化怎么办?

在D3 中,可以使用ease() 指定不同的缓动类型。默认的缓动效果是"cubic-inout",产生的就是我们刚刚看到的那种逐渐加速然后再逐渐减速的效果。

所以我们只需要设置一下ease("linear")就可以了。我们要在transition() 之后、attr() 之前指定ease()。事实上,ease()在duration() 之前之后都没问题,但先过渡再设置缓动似乎更顺理成章。

  1. ... // 选择元素的代码
  2. .transition()
  3. .duration(2000)
  4. .ease("linear")
  5. ... //attr() 的代码

linear是线性缓动,就是没有逐渐加速和减速的变化,所有元素都按照一个速度变化,变化到最终值时戛然而止。

除此之外,还有很多缓动函数可供选择。下面只是几个,不是全部
• circle
逐渐进入并加速,然后突然停止。
• elastic
描述这个效果的一个最恰当的词是“有弹性”。
• bounce
像皮球落地一样反复弹跳,慢慢停下来。

详细可以查看:https://github.com/mbostock/d3/wiki/Transitions#wiki-d3_ease

我们再来想一下,如果这个动画,我不想让它一开始就跑起来,我想让它晚点跑起来,比如说2秒后。

那我们只需要在添加一个方法.delay(2000),就OK了

  1. ...
  2. .transition()
  3. .delay(2000) //2000 毫秒,即2 秒
  4. .duration(2000) //2000 毫秒,即2 秒
  5. ...

与使用duration() 和ease() 一样,把delay() 放到哪里并没有十分严格的限制,但我更喜欢把它放在duration() 前面。因为是先设定延迟时间,然后过渡动画才开始计时,这样比较符合逻辑。

上面的代码是静态延时,静态延迟时间只是一种延迟方式,更有意思的是可以动态计算延迟时间。动态延迟的一个常见用途就是创建交错延迟的效果,让某些过渡在另一个过渡之前发生。交错延迟对人的感知有利,因为当相邻元素的变化不那么同步时,人眼更容易注意到每个元素的变化。要设置动态延迟,就别给delay() 传递静态值,而是给它传入一个函数,按照D3 的惯例……对,就是传入一个匿名函数。

  1. ...
  2. .transition()
  3. .delay(function(d, i) {
  4. return i * 100;
  5. })
  6. .duration(500)
  7. ...

在匿名函数中,与当前元素绑定的数据值是以d 传入的,而这个元素的位置是以i传入的。因此,这里的意思是让D3 循环遍历每个元素,把它们的动画延迟时间设定为i * 100,也就是后一个元素的动画开始时间总比前一个元素晚100 毫秒。

到这里,也许你会提出一个疑问,如果变化的时候数据超出了原来的范围,怎么办?

那我们就需要更新比例尺了,更新比例尺的代码很简单。

  1. // 更新比例尺的值域
  2. y.domain([0, d3.max(dataset)]);

今天就先到这里,写博客时间长了,觉得有点腰疼。

等有时间再继续更新吧~~

继续写这篇博客,就不另起一篇了~~

迄今为止,只要更新数据,我们采用的都是“整批整包”的方式:改变数据集数组中 的值,然后重新绑定修改后的值,覆盖原始值对DOM 元素的绑定。这种方式非常适合所有值都会改变,而且数据集长度(即数据值的数量)不变的情形。可是我们知道,现实中的数据可没那么简单。这就对代码的灵活 性提出了更高要求,比如只更新一两个值,或者支持增加值和减少值。

添加值(和元素)

首先我们需要在数据中插入一个值,并更新一下数轴的值域。

  1. dataset.push(10);
  2. x.domain(d3.range(dataset.length));

然后选出之前的元素,然后插入,插入的代码跟刚开始的代码很像。

  1. // 加入……
  2. var bars = svg.selectAll("rect")
  3. .data(dataset);// 选择出之前的元素
  4. bars.data(dataset)
  5. .enter()
  6. .append("rect")
  7. .attr("x", x(dataset.length - 1))//这行代码设定了新条形的水平位置,让它恰好位于SVG 区域的最右边。
  8. .attr("y", function(d) {
  9. return y(d);
  10. })
  11. .attr("width", x.rangeBand())
  12. .attr("height", function(d) {
  13. return height - y(d);
  14. })
  15. .attr("fill", function(d){
  16. return "rgb(60, 127, " + d * 10 + ")";
  17. });

最后,再更新所有的数据就OK了

  1. bars.transition()
  2. .duration(500)
  3. .attr("x", function(d, i) {
  4. return x(i);
  5. })
  6. .attr("y", function(d) {
  7. return y(d);
  8. })
  9. .attr("width", x.rangeBand())
  10. .attr("height", function(d) {
  11. return height - y(d);
  12. })
  13. .attr("fill", function(d){
  14. return "rgb(60, 127, " + d * 10 + ")";
  15. });

这样,动态的添加一条数据就实现了,然后你会发现数据的坐标轴的刻度没有变,那就需要再去更新一下坐标轴的刻度。在更新值域的代码后面,添加如下代码

  1. xAxis.scale(x);
  2. svg.select("g.x.axis")
  3. .call(xAxis);

你会发现坐标轴的刻度也跟着变化了~~

删除值(和元素)

把每次单击时添值到数据集,改为使用shift() 方法从数组中删除第一个元素。

  1. // 从数据集中删除一个值
  2. dataset.shift();

然后取得退出元素集,然后把它们过渡到右边,最后,删除它们

  1. // 退出……
  2. bars.exit()
  3. .transition()
  4. .duration(500)
  5. .attr("x", w)
  6. .remove();

remove() 是一特殊的过渡方法,它会在过渡完成后从DOM 中永远地删除元素。移除之后坐标轴的更新和之前的添加元素相同。

通过键联结数据

在把数据绑定到DOM 元素时(即调用data() 时),就会发生数据联结。默认的联结是按照索引顺序,即第一个值绑定到元素集中第一个DOM 元素,第二个值绑定到元素集中第二个DOM 元素,依此类推。如果数据值与DOM 元素的顺序不一样呢?那就得告诉D3 怎么实现值和元素间的联结或配对。好在,通过定义键函数(key:function),可以指定相应的规则。

先准备数据,之前,我们的数据集就是包含简单数值的数组。而为了使用键函数,每个值必须有对应的键。

  1. var dataset = [
    { key: 0, value: 5 },
  2. { key: 1, value: 10 },
  3. { key: 2, value: 13 },
  4. { key: 3, value: 19 },
  5. { key: 4, value: 21 },
  6. { key: 5, value: 25 },
  7. { key: 6, value: 22 },
  8. { key: 7, value: 18 },
  9. { key: 8, value: 15 },
  10. { key: 9, value: 13 },
  11. { key: 10, value: 11 },
  12. { key: 11, value: 12 },
  13. { key: 12, value: 15 },
  14. { key: 13, value: 20 },
  15. { key: 14, value: 18 },
  16. { key: 15, value: 17 },
  17. { key: 16, value: 16 },
  18. { key: 17, value: 18 },
  19. { key: 18, value: 23 },
  20. { key: 19, value: 25 }
    ];

更新引用,许多以前用d的地方要修改为d.value,如

  1. var y = d3.scale.linear()
  2. .domain([0, d3.max(dataset, function(d) { return d.value;})])
  3. .range([0, height]);

链接键函数,在data一个数据集时,使用如下代码,就OK了。

  1. .data(dataset, function(d) {
  2. return d.key;
  3. })

关于数据的更新和动画就先到这里为止了,之后会写D3的交互和布局~~

数据可视化(8)--D3数据的更新及动画的更多相关文章

  1. 数据可视化(7)--D3基础

    一直想写写D3,觉得D3真心比较强大,基本上你能想出来的图表都能绘制出来,只不过使用起来比前几个要稍麻烦一点. 正好最近读完了<数据可视化实战>,将关于D3的知识梳理了一遍,写这篇博客记录 ...

  2. 交互式数据可视化-D3.js(四)形状生成器

    使用JavaScript和D3.js实现数据可视化 形状生成器 线段生成器 var linePath = d3.line() - 使用默认的设置构造一个 line 生成器. linePath.x() ...

  3. 交互式数据可视化-D3.js(二)选择集和数据

    选择集 select和selectAll类似jquery: d3.select('body') d3.select('.body') d3.select('#body') d3.selectAll(' ...

  4. 交互式数据可视化-D3.js(三)比例尺

    线性比例尺 线性比例尺是常用比例尺常用方法有: var linear = d3.scaleLinear() - 创建一个定量的线性比例尺. linear.domain([numbers]) - 定义或 ...

  5. <数据可视化>样例+数据+画图

    1 样例 1.1样例1 子图系列 from pylab import * def f(x): return np.exp(-x) * np.cos(2*np.pi*x) x1 = np.arange( ...

  6. d3.js:数据可视化利器之快速入门

    hello,data! 在进入d3.js之前,我们先用一个小例子回顾一下将数据可视化的基本流程. 任务 用横向柱状图来直观显示以下数据: var data = [10,15,23,78,57,29,3 ...

  7. PoPo数据可视化周刊第5期

    PoPo数据可视化 聚焦于Web数据可视化与可视化交互领域,发现可视化领域有意思的内容.不想错过可视化领域的精彩内容, 就快快关注我们吧 :) World Wire 数据可视化演示(视频) IBM公司 ...

  8. Webservice WCF WebApi 前端数据可视化 前端数据可视化 C# asp.net PhoneGap html5 C# Where 网站分布式开发简介 EntityFramework Core依赖注入上下文方式不同造成内存泄漏了解一下? SQL Server之深入理解STUFF 你必须知道的EntityFramework 6.x和EntityFramework Cor

    Webservice WCF WebApi   注明:改编加组合 在.net平台下,有大量的技术让你创建一个HTTP服务,像Web Service,WCF,现在又出了Web API.在.net平台下, ...

  9. 数据可视化Echarts-实例

    数据可视化 Echarts 百度 数据可视化 hightCharts 1 数据可视化 D3 老外 -----------------------------当遇到个啥玩意儿,Echarts .high ...

随机推荐

  1. [置顶]PADS PCB功能使用技巧系列之NO.003- 如何统一修改元件标号字体?

    LAYOUT完毕后进行元件标号字体调整时,你是否试图用Select Document+Select All来选定所有标号?可结果却并不令人满意. (1)在Layout中,选择菜单栏Edit -> ...

  2. 正则表达式统计java代码空白行,有效代码

    import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import jav ...

  3. mysql 基本操作语句

    mysql 基本操作笔记: 创建表demo:CREATE TABLE `role` ( `role_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMME ...

  4. 5.webService拦截器

    CXF为什么要设计拦截器? 为了在webservice请求过程中,能动态操作请求和响应数据, CXF设计了拦截器. 拦截器分类 1.按所处的位置分:服务器端拦截器,客户端拦截器 2.按消息的方向分:入 ...

  5. Ajax的同步与异步

    原文地址:http://www.cnblogs.com/Joetao/articles/3525007.html <%@ Page Language="C#" AutoEve ...

  6. ubuntu常用配置

    安装文件共享服务 0.更改本机主机名,修改 /etc/hostname文件(ubuntu默认都是ubuntu) 1.安装 #sudo apt-get install samba samba-commo ...

  7. 如何向MyEclipse项目的文件夹中添加JSP页面?

    1.鼠标选中该文件夹:2.鼠标右键单击该文件夹,选择“new->file”菜单项,创建一个空白文件:3.将JSP页面的所有源代码全部复制到该文件4.单击工具栏的“保存”按钮

  8. 我的ORM之十三 -- 性能参数

    我的ORM索引 测试环境 台式机: 主板:映泰Z77 CPU:i5 3470(3.2GHz) 内存:DDR3 1600 8G(单条) 硬盘:创见 SSD 256G ORM从过程上,可以分两个大的部分: ...

  9. 算法:POJ1008 Maya Calendar

    此题非常水,不做说明. package practice; import java.io.BufferedInputStream; import java.util.Scanner; /** * @a ...

  10. 精神哥讲Crash(一):UnsatisfiedLinkError

    版权声明:本文为腾讯Bugly原创文章,如需转载,请标明出处.   大家好,我是腾讯Bugly的精神哥(英文名:spirit),是Bugly资深码奴的同时,又是Bugly神秘的Crash实验室研究员哦 ...