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树状图异步按需加载数据的主要内容,如果未能解决你的问题,请参考以下文章