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

Posted SHIHUC

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 

以上是关于D3树状图异步按需加载数据的主要内容,如果未能解决你的问题,请参考以下文章

d3js树状图tree

D3.js的V5版本-Vue框架中使用-树状图

d3.js(v5.7)树状图

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

d3生成的树状图

vue 异步组件 (按需加载)