zoukankan      html  css  js  c++  java
  • 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- 1.5px;
     13 }
     14 
     15 .node text {
     16   font: 10px sans-serif;
     17 }
     18 
     19 .link {
     20   fill: none;
     21   stroke: #ccc;
     22   stroke- 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- 1.5px;
     13 }
     14 
     15 .node text {
     16   font: 10px sans-serif;
     17 }
     18 
     19 .link {
     20   fill: none;
     21   stroke: #ccc;
     22   stroke- 1.5px;
     23 }
     24 
     25 .link2 {
     26   fill: none;
     27   stroke: #f00;
     28   stroke- 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 }

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

  • 相关阅读:
    Behavior Targeting 技术研究
    LINQ的Distinct总结
    当为业务逻辑层(BLL,Service)提供基类之后,程序变得了更加面向对象了
    MVC中,查询以异步呈现,分页不用异步的解决方案
    基础才是重中之重——面向抽象编程(抽象类的多态性)
    基础才是重中之重——派生类集合与基类集合可以相互转换吗?
    让你的 Nginx 的 RTMP 直播具有统计某频道在线观看用户数量的功能
    转发一个最新HEVCH.265码流分析软件
    PHP性能优化大全(整理)
    “灭”了国外的mini sql !
  • 原文地址:https://www.cnblogs.com/shihuc/p/6064448.html
Copyright © 2011-2022 走看看