zoukankan      html  css  js  c++  java
  • D3树状图给指定特性的边特别显示颜色

    D3作为前端图形显示的利器,功能之强,对底层技术细节要求相对比较多。

    有一点,就是要理解其基本的数据和节点的匹配规则架构,即enter,update和exit原理,我前面的D3基础篇中有介绍过,不明白的可以再去研究下。

    本篇博文,同样是在这个框架下,完成修改树状图中某两个节点之间的边用红色线条连接,实现表达特殊含义的目的。

    背景故事: 微信朋友圈之间产品帖子相互转发,有些帖子转发后会有成交,只要有成交,则这个促成成交的节点及其之上的父节点都相应是有功劳的,这个轨迹需要用高亮的颜色表示(比如本例中,用红色表示)。

    其实也比较简单,直接看代码, 前端部分:

      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 //Redraw for zoom
     84 function redraw() {
     85   //console.log("here", d3.event.translate, d3.event.scale);
     86   svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
     87 }
     88     
     89 var svg = d3.select("body").append("svg").attr("width", 1024).attr("height", 798)
     90     .call(zm = d3.behavior.zoom().scaleExtent([1,3]).on("zoom", redraw)).append("g")
     91     .attr("transform", "translate(" + 512 + "," + 50 + ")");
     92 
     93 //necessary so that zoom knows where to zoom and unzoom from
     94 zm.translate([512, 50]);
     95     
     96 //d3.json("flare.json", function(error, flare) 
     97 //  if (error) throw error;
     98   
     99 root.x0 = 0;
    100 root.y0 = height / 2;
    101 
    102 function collapse(d) {
    103     if (d.children) {
    104       d._children = d.children;
    105       d._children.forEach(collapse);
    106       d.children = null;
    107     }
    108 }
    109 
    110 root.children.forEach(collapse);
    111 update(root);
    112 
    113 
    114 d3.select(self.frameElement).style("height", "800px");
    115 
    116 function update(source) {
    117 
    118   // Compute the new tree layout.
    119   var nodes = tree.nodes(root).reverse(),
    120       links = tree.links(nodes);
    121 
    122   // Normalize for fixed-depth.
    123   nodes.forEach(function(d) { d.y = d.depth * 180; });
    124 
    125   // Update the nodes…
    126   var node = svg.selectAll("g.node")
    127       .data(nodes, function(d) { return d.id || (d.id = ++i); });
    128 
    129   // Enter any new nodes at the parent's previous position.
    130   var nodeEnter = node.enter().append("g")
    131       .attr("class", "node")
    132       .attr("transform", function(d) { return "translate(" + source.x0 + "," + source.y0 + ")"; })
    133       .on("click", click);
    134 
    135   nodeEnter.append("circle")
    136       .attr("r", 1e-6)
    137       .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
    138 
    139   nodeEnter.append("text")
    140       .attr("cx", function(d) { return d.children || d._children ? -10 : 10; })
    141       .attr("cy", ".35em")
    142       .attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
    143       .text(function(d) { return d.name; })
    144       .style("fill-opacity", 1e-6);
    145 
    146   // Transition nodes to their new position.
    147   var nodeUpdate = node.transition()
    148       .duration(duration)
    149       .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
    150 
    151   nodeUpdate.select("circle")
    152       .attr("r", 20)
    153       .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
    154 
    155   nodeUpdate.select("text")
    156       .style("fill-opacity", 1);
    157 
    158   // Transition exiting nodes to the parent's new position.
    159   var nodeExit = node.exit().transition()
    160       .duration(duration)
    161       .attr("transform", function(d) { return "translate(" + source.x + "," + source.y + ")"; })
    162       .remove();
    163 
    164   nodeExit.select("circle")
    165       .attr("r", 1e-6);
    166 
    167   nodeExit.select("text")
    168       .style("fill-opacity", 1e-6);
    169 
    170   // Update the links…
    171   var link = svg.selectAll("path.link")
    172       .data(links, function(d) { return d.target.id; });
    173 
    174   // Enter any new links at the parent's previous position.
    175   link.enter().insert("path", "g")
    176   .attr("class", "link")
    177   .attr("d", function(d) {
    178     var o = {x: source.x0, y: source.y0};
    179     return diagonal({source: o, target: o});
    180   });
    181   
    182   // Transition links to their new position.
    183   link.transition()
    184       .duration(duration)
    185       .attr("d", diagonal)
    186       .attr("class", function(d){
    187           if(d.source.deal != null && d.source.deal != undefined){
    188             if(d.target.deal != null && d.target.deal != undefined){
    189                 return "link link2";
    190             }
    191         }
    192         return "link";
    193       });
    194 
    195   // Transition exiting nodes to the parent's new position.
    196   link.exit().transition()
    197       .duration(duration)
    198       .attr("d", function(d) {
    199         var o = {x: source.x, y: source.y};
    200         return diagonal({source: o, target: o});
    201       })
    202       .remove();
    203 
    204   // Stash the old positions for transition.
    205   nodes.forEach(function(d) {
    206     d.x0 = d.x;
    207     d.y0 = d.y;
    208   });
    209 }
    210 
    211 
    212 function getNode(){
    213     var mynodes = null;
    214     $.ajax({  
    215         url : "./node",  
    216         async : false, // 注意此处需要同步,因为返回完数据后,下面才能让结果的第一条selected  
    217         type : "POST",  
    218         dataType : "json",  
    219         success : function(data) {            
    220             mynodes = data;
    221             console.log(mynodes);            
    222             //nodes = JSON.parse(nodes);
    223         }  
    224     });  
    225     return mynodes;
    226 }
    227 
    228 // Toggle children on click.
    229 function click(d) {
    230       if (d.children) {
    231         d._children = d.children;
    232         d.children = null;
    233       } else if(d._children){         
    234         d.children = d._children;
    235         d._children = null;
    236       }else {          
    237          var mnodes = getNode();
    238         d.children = mnodes.children;        
    239       }
    240   update(d);
    241 }
    242 
    243 </script>

    整个前端的代码,重点看其中红色标识的部分,这些部分是和这个博文的内容直接相关的。 涉及到连接的红色标识。 数据中定义了deal字段,这个字段就是标识某个节点具有这个特性,只有这个特性的节点之间的边用红色标识。 另外,点击按钮,异步加载后端服务器的代码部分,也有部分数据是含有这个deal特性的,同样适用于本故事的要求。

    在代码的186行中,link的transition(即变化,变换)过程中,去渲染节点之间的边的样式。其实,还可以在其他地方做这个样式的加载,比如在link的enter部分实现,只是这个过程,有点违背D3架构设计之enter,update和exit的大前提,不建议在enter里面实现这个功能。

    后端的代码,只是一个示例代码,和前面D3博文的基本相同:

     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 "d3demo2";
    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 }

    上述代码,表明只有节点名为one的节点,给其配置deal属性值,也就是说在最终D3绘制的树状图上,名为one的节点间会出现红色link。

    最终效果图如下,首先看默认显示的情况

    点击显示one节点之间的状态

  • 相关阅读:
    SpringBoot第十七篇:定时任务
    20年研发管理经验谈(十)
    SpringBoot第十六篇:自定义starter
    20年研发管理经验谈(九)
    20年研发管理经验谈(八)
    20年研发管理经验谈(七)
    SpringBoot第十五篇:swagger构建优雅文档
    CSS聊天气泡
    Java单例模式
    Java观察者模式
  • 原文地址:https://www.cnblogs.com/shihuc/p/6150526.html
Copyright © 2011-2022 走看看