D3树状图异步按需加载数据

D3.js这个绘图工具,功能强大不必多说,完全一个Data Driven Document的绘图工具,用户可以按照自己的数据以及希望实现的图形,随心所欲的绘图。

图形绘制,D3默认采用的是异步加载,但是,这里的异步加载,指的是一次性的将图形展示所需要的数据异步的方式加载到浏览器前端显示。主要有如下这两种方式:

 1 d3.csv(url[[, row], callback])
 2
 3 Creates a request for the CSV file at the specified url with the default mime type text/csv. An optional row conversion function may be specified to map and filter row objects to a more-specific representation; see dsv.parse for details.
 4
 5 The row conversion function can be changed by calling request.row on the returned instance. For example, this:
 6
 7 d3.csv(url, row, callback);
 8 Is equivalent to this:
 9
10 d3.csv(url)
11     .row(row)
12     .get(callback);
 1 d3.json(url[, callback])
 2
 3 Creates a request for the JSON file at the specified url with the default mime type application/json.
 4
 5 This convenience constructor is approximately equivalent to:
 6
 7 d3.request(url)
 8     .mimeType("application/json")
 9     .response(function(xhr) { return JSON.parse(xhr.responseText); })
10     .get(callback);

上述两种方式获取的数据,在很多时候,是比较难满足实际需求场景的。

比如,我们现在设计的一款微信公众号的应用中,捕获关注者转帖的轨迹,最终以树状图展现给用户。 若一次性加载所有的数据,会比较影响用户体验,因为一次遍历数据库所有的跟踪记录,无论是递归先根遍历还是非递归方式循环查找,最终的体验都是不令人满意的。 我们便采取按需的异步加载数据方式,即,当用户点击节点时,才从后台取数据。由于D3的优秀数据管理架构,数据一旦加载了,后续便可以不用再从服务器后台取数据。

其实,实现这种on demand方式的异步加载,其实也很简单。下面就基于官网的一个例子,做点修改,介绍如何实现。

官网原版的例子如下:

  1 <!DOCTYPE html>
  2 <meta charset="utf-8">
  3 <style>
  4
  5 .node {
  6   cursor: pointer;
  7 }
  8
  9 .node circle {
 10   fill: #fff;
 11   stroke: steelblue;
 12   stroke-width: 1.5px;
 13 }
 14
 15 .node text {
 16   font: 10px sans-serif;
 17 }
 18
 19 .link {
 20   fill: none;
 21   stroke: #ccc;
 22   stroke-width: 1.5px;
 23 }
 24
 25 </style>
 26 <body>
 27 <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
 28 <script>
 29 var root = {
 30     "name": "flare",
 31     "deal": "2",
 32     "children": [{
 33             "name": "analytics" ,
 34             "children": [{
 35                 "name": "cluster",
 36                 "children": [{
 37                      "name": "AgglomerativeCluster",
 38                      "size": 3938
 39                 }, {
 40                     "name": "CommunityStructure",
 41                     "size": 3812
 42                 }, {
 43                     "name": "HierarchicalCluster",
 44                     "size": 6714
 45                 }, {
 46                     "name": "MergeEdge",
 47                     "size": 743
 48                 }]
 49             }]
 50         }, {
 51             "name": "ISchedulable",
 52             "deal": "2",
 53             "size": 1041
 54         }, {
 55             "name": "Parallel",
 56             "size": 5176
 57         }, {
 58             "name": "Pause",
 59             "size": 449
 60         }
 61     ]
 62 };
 63 var margin = {top: 20, right: 120, bottom: 20, left: 120},
 64     width = 1024 - margin.right - margin.left,
 65     height = 798 - margin.top - margin.bottom;
 66
 67 var i = 0,
 68 duration = 750,
 69 root;
 70
 71 var tree = d3.layout.tree().nodeSize([90, 60]);
 72
 73 var diagonal = d3.svg.diagonal()
 74     .projection(function(d) { return [d.x, d.y]; });
 75
 76 /*
 77 var svg = d3.select("body").append("svg")
 78     .attr("width", width + margin.right + margin.left)
 79     .attr("height", height + margin.top + margin.bottom)
 80   .append("g")
 81     .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
 82 */
 83
 84 //Redraw for zoom
 85 function redraw() {
 86   //console.log("here", d3.event.translate, d3.event.scale);
 87   svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
 88 }
 89
 90 var svg = d3.select("body").append("svg").attr("width", 1024).attr("height", 798)
 91     .call(zm = d3.behavior.zoom().scaleExtent([1,3]).on("zoom", redraw)).append("g")
 92     .attr("transform", "translate(" + 512 + "," + 50 + ")");
 93
 94 //necessary so that zoom knows where to zoom and unzoom from
 95 zm.translate([512, 50]);
 96
 97 //d3.json("flare.json", function(error, flare)
 98 //  if (error) throw error;
 99 {
100 root.x0 = 0;
101 root.y0 = height / 2;
102
103   function collapse(d) {
104     if (d.children) {
105       d._children = d.children;
106       d._children.forEach(collapse);
107       d.children = null;
108     }
109   }
110
111   root.children.forEach(collapse);
112   update(root);
113 }
114
115 d3.select(self.frameElement).style("height", "800px");
116
117 function update(source) {
118
119   // Compute the new tree layout.
120   var nodes = tree.nodes(root).reverse(),
121       links = tree.links(nodes);
122
123       debugger;
124   // Normalize for fixed-depth.
125   nodes.forEach(function(d) { d.y = d.depth * 180; });
126
127   // Update the nodes…
128   var node = svg.selectAll("g.node")
129       .data(nodes, function(d) { return d.id || (d.id = ++i); });
130
131   // Enter any new nodes at the parent‘s previous position.
132   var nodeEnter = node.enter().append("g")
133       .attr("class", "node")
134       .attr("transform", function(d) { return "translate(" + source.x0 + "," + source.y0 + ")"; })
135       .on("click", click);
136
137   nodeEnter.append("circle")
138       .attr("r", 1e-6)
139       .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
140
141   nodeEnter.append("text")
142       .attr("x", function(d) { return d.children || d._children ? -10 : 10; })
143       .attr("dy", ".35em")
144       .attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
145       .text(function(d) { return d.name; })
146       .style("fill-opacity", 1e-6);
147
148   // Transition nodes to their new position.
149   var nodeUpdate = node.transition()
150       .duration(duration)
151       .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
152
153   nodeUpdate.select("circle")
154       .attr("r", 20)
155       .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
156
157   nodeUpdate.select("text")
158       .style("fill-opacity", 1);
159
160   // Transition exiting nodes to the parent‘s new position.
161   var nodeExit = node.exit().transition()
162       .duration(duration)
163       .attr("transform", function(d) { return "translate(" + source.x + "," + source.y + ")"; })
164       .remove();
165
166   nodeExit.select("circle")
167       .attr("r", 1e-6);
168
169   nodeExit.select("text")
170       .style("fill-opacity", 1e-6);
171
172   // Update the links…
173   var link = svg.selectAll("path.link")
174       .data(links, function(d) { return d.target.id; });
175
176   // Enter any new links at the parent‘s previous position.
177   link.enter().insert("path", "g")
178       .attr("class", "link")
179       .attr("d", function(d) {
180         var o = {x: source.x0, y: source.y0};
181         return diagonal({source: o, target: o});
182       });
183
184   // Transition links to their new position.
185   link.transition()
186       .duration(duration)
187       .attr("d", diagonal);
188
189   // Transition exiting nodes to the parent‘s new position.
190   link.exit().transition()
191       .duration(duration)
192       .attr("d", function(d) {
193         var o = {x: source.x, y: source.y};
194         return diagonal({source: o, target: o});
195       })
196       .remove();
197
198   // Stash the old positions for transition.
199   nodes.forEach(function(d) {
200     d.x0 = d.x;
201     d.y0 = d.y;
202   });
203 }
204
205 // Toggle children on click.
206 function click(d) {
207   if (d.children) {
208     d._children = d.children;
209     d.children = null;
210   } else {
211     d.children = d._children;
212     d._children = null;
213   }
214   update(d);
215 }
216
217 </script>

下面,再看看,如何实现on demand的异步加载:

  1 <!DOCTYPE html>
  2 <meta charset="utf-8">
  3 <style>
  4
  5 .node {
  6   cursor: pointer;
  7 }
  8
  9 .node circle {
 10   fill: #fff;
 11   stroke: steelblue;
 12   stroke-width: 1.5px;
 13 }
 14
 15 .node text {
 16   font: 10px sans-serif;
 17 }
 18
 19 .link {
 20   fill: none;
 21   stroke: #ccc;
 22   stroke-width: 1.5px;
 23 }
 24
 25 .link2 {
 26   fill: none;
 27   stroke: #f00;
 28   stroke-width: 1.5px;
 29 }
 30
 31 </style>
 32 <body>
 33 <script src="js/jquery-2.1.1.min.js" charset="utf-8"></script>
 34 <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
 35 <script>
 36 var root = {
 37     "name": "flare",
 38     "deal": "2",
 39     "children": [{
 40             "name": "analytics" ,
 41             "children": [{
 42                 "name": "cluster",
 43                 "children": [{
 44                      "name": "AgglomerativeCluster",
 45                      "size": 3938
 46                 }, {
 47                     "name": "CommunityStructure",
 48                     "size": 3812
 49                 }, {
 50                     "name": "HierarchicalCluster",
 51                     "size": 6714
 52                 }, {
 53                     "name": "MergeEdge",
 54                     "size": 743
 55                 }]
 56             }]
 57         }, {
 58             "name": "ISchedulable",
 59             "deal": "2",
 60             "size": 1041
 61         }, {
 62             "name": "Parallel",
 63             "size": 5176
 64         }, {
 65             "name": "Pause",
 66             "size": 449
 67         }
 68     ]
 69 };
 70 var margin = {top: 20, right: 120, bottom: 20, left: 120},
 71     width = 1024 - margin.right - margin.left,
 72     height = 798 - margin.top - margin.bottom;
 73
 74 var i = 0,
 75 duration = 750,
 76 root;
 77
 78 var tree = d3.layout.tree().nodeSize([90, 60]);
 79
 80 var diagonal = d3.svg.diagonal()
 81     .projection(function(d) { return [d.x, d.y]; });
 82
 83 /*
 84 var svg = d3.select("body").append("svg")
 85     .attr("width", width + margin.right + margin.left)
 86     .attr("height", height + margin.top + margin.bottom)
 87   .append("g")
 88     .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
 89 */
 90
 91 //Redraw for zoom
 92 function redraw() {
 93   //console.log("here", d3.event.translate, d3.event.scale);
 94   svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
 95 }
 96
 97 var svg = d3.select("body").append("svg").attr("width", 1024).attr("height", 798)
 98     .call(zm = d3.behavior.zoom().scaleExtent([1,3]).on("zoom", redraw)).append("g")
 99     .attr("transform", "translate(" + 512 + "," + 50 + ")");
100
101 //necessary so that zoom knows where to zoom and unzoom from
102 zm.translate([512, 50]);
103
104 //d3.json("flare.json", function(error, flare)
105 //  if (error) throw error;
106
107 root.x0 = 0;
108 root.y0 = height / 2;
109
110 function collapse(d) {
111     if (d.children) {
112       d._children = d.children;
113       d._children.forEach(collapse);
114       d.children = null;
115     }
116 }
117
118 root.children.forEach(collapse);
119 update(root);
120
121
122 d3.select(self.frameElement).style("height", "800px");
123
124 function update(source) {
125
126   // Compute the new tree layout.
127   var nodes = tree.nodes(root).reverse(),
128       links = tree.links(nodes);
129
130   // Normalize for fixed-depth.
131   nodes.forEach(function(d) { d.y = d.depth * 180; });
132
133   // Update the nodes…
134   var node = svg.selectAll("g.node")
135       .data(nodes, function(d) { return d.id || (d.id = ++i); });
136
137   // Enter any new nodes at the parent‘s previous position.
138   var nodeEnter = node.enter().append("g")
139       .attr("class", "node")
140       .attr("transform", function(d) { return "translate(" + source.x0 + "," + source.y0 + ")"; })
141       .on("click", click);
142
143   nodeEnter.append("circle")
144       .attr("r", 1e-6)
145       .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
146
147   nodeEnter.append("text")
148       .attr("cx", function(d) { return d.children || d._children ? -10 : 10; })
149       .attr("cy", ".35em")
150       .attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
151       .text(function(d) { return d.name; })
152       .style("fill-opacity", 1e-6);
153
154   // Transition nodes to their new position.
155   var nodeUpdate = node.transition()
156       .duration(duration)
157       .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
158
159   nodeUpdate.select("circle")
160       .attr("r", 20)
161       .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
162
163   nodeUpdate.select("text")
164       .style("fill-opacity", 1);
165
166   // Transition exiting nodes to the parent‘s new position.
167   var nodeExit = node.exit().transition()
168       .duration(duration)
169       .attr("transform", function(d) { return "translate(" + source.x + "," + source.y + ")"; })
170       .remove();
171
172   nodeExit.select("circle")
173       .attr("r", 1e-6);
174
175   nodeExit.select("text")
176       .style("fill-opacity", 1e-6);
177
178   // Update the links…
179   var link = svg.selectAll("path.link")
180       .data(links, function(d) { return d.target.id; });
181
182   // Enter any new links at the parent‘s previous position.
183   link.enter().insert("path", "g")
184       .attr("class", "link")
185       .attr("d", function(d) {
186         var o = {x: source.x0, y: source.y0};
187         return diagonal({source: o, target: o});
188       });
189   /*
190   console.log(link);
191
192   link.enter().insert("path", "g")
193   .attr("class", function(d){
194       if(d.source.deal != null && d.source.deal != undefined){
195             if(d.target.deal != null && d.target.deal != undefined){
196                 return "link2";
197             }
198         }
199         return "link";
200       })
201   .attr("d", function(d) {
202     var o = {x: source.x0, y: source.y0};
203     return diagonal({source: o, target: o});
204   });
205   */
206   // Transition links to their new position.
207   link.transition()
208       .duration(duration)
209       .attr("d", diagonal);
210
211   // Transition exiting nodes to the parent‘s new position.
212   link.exit().transition()
213       .duration(duration)
214       .attr("d", function(d) {
215         var o = {x: source.x, y: source.y};
216         return diagonal({source: o, target: o});
217       })
218       .remove();
219
220   // Stash the old positions for transition.
221   nodes.forEach(function(d) {
222     d.x0 = d.x;
223     d.y0 = d.y;
224   });
225 }
226
227 function getNode(){       #自定义的一个新的以同步方式从后台取数据的ajax函数
228     var mynodes = null;
229     $.ajax({
230         url : "./node",
231         async : false, // 注意此处需要同步
232         type : "POST",
233         dataType : "json",
234         success : function(data) {
235             mynodes = data;
236             console.log(mynodes);
237             //nodes = JSON.parse(nodes);
238         }
239     });
240     return mynodes;
241 }
242
243 // Toggle children on click.
244 function click(d) {          #重点关注这个函数的不同之处。尤其是else部分
245       if (d.children) {
246         d._children = d.children;
247         d.children = null;
248       } else if(d._children){
249         d.children = d._children;
250         d._children = null;
251       }else {
252          var mnodes = getNode();
253         d.children = mnodes.children;
254       }
255   update(d);
256 }
257
258 </script>

配合这个ajax的函数,java后台的代码,其实非常的简单,为了测试,构建D3树状图所需的数据结构。主要都是Array (含有孩子节点)

 1 /**
 2  * @author "shihuc"
 3  * @date   2016年11月14日
 4  */
 5 package com.tk.es.search.controller;
 6
 7 import java.util.ArrayList;
 8 import java.util.HashMap;
 9
10 import javax.servlet.http.HttpServletRequest;
11
12 import org.springframework.stereotype.Controller;
13 import org.springframework.web.bind.annotation.RequestMapping;
14 import org.springframework.web.bind.annotation.ResponseBody;
15
16 import com.google.gson.Gson;
17
18 /**
19  * @author chengsh05
20  *
21  */
22 @Controller
23 public class D3Controller {
24
25     @RequestMapping(value = "/d3")
26     public String d3Page(HttpServletRequest req){
27         return "d3demo";
28     }
29
30     @RequestMapping(value = "/node")
31     @ResponseBody
32     public String asyncGet(HttpServletRequest req){
33         HashMap<String, Object> data = new HashMap<String, Object>();
34         ArrayList<Object>elem1 = new ArrayList<Object>();
35         HashMap<String, String> elem1e = new HashMap<String, String>();
36         elem1e.put("name", "one");
37         elem1e.put("deal", "2");
38         HashMap<String, String> elem2e = new HashMap<String, String>();
39         elem2e.put("name", "two");
40         HashMap<String, String> elem3e = new HashMap<String, String>();
41         elem3e.put("name", "three");
42         elem1.add(elem1e);
43         elem1.add(elem2e);
44         elem1.add(elem3e);
45
46         data.put("name", "Pause");
47         data.put("children", elem1);
48
49         Gson gson = new Gson();
50         return gson.toJson(data);
51     }
52 }

有一定的参考价值,需要的请转载,转载指明出处!

时间: 2024-08-07 17:01:45

D3树状图异步按需加载数据的相关文章

D3树状图给指定特性的边特别显示颜色

D3作为前端图形显示的利器,功能之强,对底层技术细节要求相对比较多. 有一点,就是要理解其基本的数据和节点的匹配规则架构,即enter,update和exit原理,我前面的D3基础篇中有介绍过,不明白的可以再去研究下. 本篇博文,同样是在这个框架下,完成修改树状图中某两个节点之间的边用红色线条连接,实现表达特殊含义的目的. 背景故事: 微信朋友圈之间产品帖子相互转发,有些帖子转发后会有成交,只要有成交,则这个促成成交的节点及其之上的父节点都相应是有功劳的,这个轨迹需要用高亮的颜色表示(比如本例中

【 D3.js 入门系列 --- 9.5 】 树状图的制作

这一节学习树状图的制作.树状图的制作和集群图完全相同,经过这两种 layout 转换后的数据也很相似. 本人的个人博客为: www.ourd3js.com csdn博客为: blog.csdn.net/lzhlzz 转载请注明出处,谢谢. 树状图( Tree )通常用于表示层级.上下级.包含与被包含关系.树状图的制作和 9.4节集群图的制作 的代码几乎完全一样.不错,你没看错,几乎完全一样.那么为什么要把这两种图分开,它们有什么不同呢?先来看看对于同一组数据,它们的结果有什么不同.数据为: {

d3.js(v5.7)树状图

一.新建画布 二.数据处理 三.绘制连接线 图示: 四.绘制节点.文字 图示: 五.总结 path元素:其实就是定义了绘图的坐标点,从哪开始,移动到哪,怎样移动(命令) 具体可百度(或许以后我会总结一篇关于path的?) 另外:此篇树状图用了博主自定义的automatch和attr(扩展版)函数,若有不明白的可参照之前的博客,避免控制台报错. 原文地址:https://www.cnblogs.com/eco-just/p/10055293.html

highcharts+jsp+springDataJpa实现饼状图,树状图

1.饼状图 1. 创建spirngboot项目,引入以下启动器. <!-- servlet 依赖. --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <scope>provided</scope> </dependency> <!-- jstl依赖. --&g

AngularJS in Action读书笔记5(实战篇)——在directive中引入D3饼状图显示

前言: "宁肯像种子一样等待  也不愿像疲惫的陀螺  旋转得那样勉强" 这是前几天在查资料无意间看到的一位园友的签名,看完后又读了两遍,觉得很有味道.后来一寻根究底才知这是出资大诗人汪国真之口,出处<她>.且抛开上下文,单从这短短几句,正恰如其分的折射出有一群人,他们穿着不那么fashion,言辞不那么犀利,但是内心某一块地方像是躁动的火山,拥有无尽的动力和激情,矢志不渝种子般投身到技术研究和心得分享当中. 或许每一次的生长都是那么悄无声息,但是无数次的坚持只是为了破土那日

Android开源图表之树状图和饼状图的官方示例的整理

最近由于工作需要,所以就在github上搜了下关于chart的三方框架 官方地址https://github.com/PhilJay/MPAndroidChart 由于工作需要我这里整理了一份Eclipse版本的类库.(有需要要的留下邮箱) 这就是Code中的效果(树状图) 1 public class BarChartActivity extends Activity implements OnChartValueSelectedListener{ 2 3 private BarChart m

bzoj 4871: [Shoi2017]摧毁“树状图”

4871: [Shoi2017]摧毁“树状图” Time Limit: 25 Sec  Memory Limit: 512 MBSubmit: 53  Solved: 9[Submit][Status][Discuss] Description 自从上次神刀手帮助蚯蚓国增添了上千万人口(蚯口?),蚯蚓国发展得越来越繁荣了!最近,他们在地下发现了 一些神奇的纸张,经过仔细研究,居然是D国X市的超级计算机设计图纸!这台计算机叫做‘树状图’,由n个计算 节点与n1条可以双向通信的网线连接而成,所有计算

一款很好用的JQuery dtree树状图插件

一款很好用的JQuery dtree树状图插件 树状图  -dtree 由于他的节点设置思想不错,使连接数据库的数据库设计比较方便. 下载dtree资源包,引用一下dtree.css和dtree.js文件,然后编写节点就行了. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quo

Linux命令之pstree - 以树状图显示进程间的关系

本文链接:http://codingstandards.iteye.com/blog/842156   (转载请注明出处) 用途说明 pstree命令以树状图显示进程间的关系(display a tree of processes).ps命令可以显示当前正在运行的那些进程的信息,但是对于它们之间的关系却显示得不够清晰.在Linux系统中,系统调用fork可以创建子进程,通过子shell也可以创建子进程,Linux系统中进程之间的关系天生就是一棵树,树的根就是进程PID为1的init进程. 常用参