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

时间:2023-03-08 16:22:33

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

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

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

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

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

 <!DOCTYPE html>
<meta charset="utf-8">
<style> .node {
cursor: pointer;
} .node circle {
fill: #fff;
stroke: steelblue;
stroke-width: .5px;
} .node text {
font: 10px sans-serif;
} .link {
fill: none;
stroke: #ccc;
stroke-width: .5px;
} .link2 {
fill: none;
stroke: #f00;
stroke-width: 1.5px;
} </style>
<body>
<script src="js/jquery-2.1.1.min.js" charset="utf-8"></script>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
var root = {
"name": "flare",
"deal": "2",
"children": [{
"name": "analytics" ,
"children": [{
"name": "cluster",
"children": [{
"name": "AgglomerativeCluster",
"size":
}, {
"name": "CommunityStructure",
"size":
}, {
"name": "HierarchicalCluster",
"size":
}, {
"name": "MergeEdge",
"size":
}]
}]
}, {
"name": "ISchedulable",
"deal": "2",
"size":
}, {
"name": "Parallel",
"size":
}, {
"name": "Pause",
"size":
}
]
};
var margin = {top: , right: , bottom: , left: },
width = - margin.right - margin.left,
height = - margin.top - margin.bottom; var i = ,
duration = ,
root; var tree = d3.layout.tree().nodeSize([, ]); var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.x, d.y]; }); //Redraw for zoom
function redraw() {
//console.log("here", d3.event.translate, d3.event.scale);
svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
} var svg = d3.select("body").append("svg").attr("width", ).attr("height", )
.call(zm = d3.behavior.zoom().scaleExtent([,]).on("zoom", redraw)).append("g")
.attr("transform", "translate(" + + "," + + ")"); //necessary so that zoom knows where to zoom and unzoom from
zm.translate([, ]); //d3.json("flare.json", function(error, flare)
// if (error) throw error; root.x0 = ;
root.y0 = height / ; function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
} root.children.forEach(collapse);
update(root); d3.select(self.frameElement).style("height", "800px"); function update(source) { // Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),
links = tree.links(nodes); // Normalize for fixed-depth.
nodes.forEach(function(d) { d.y = d.depth * ; }); // Update the nodes…
var node = svg.selectAll("g.node")
.data(nodes, function(d) { return d.id || (d.id = ++i); }); // Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + source.x0 + "," + source.y0 + ")"; })
.on("click", click); nodeEnter.append("circle")
.attr("r", 1e-)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; }); nodeEnter.append("text")
.attr("cx", function(d) { return d.children || d._children ? - : ; })
.attr("cy", ".35em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text(function(d) { return d.name; })
.style("fill-opacity", 1e-); // Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); nodeUpdate.select("circle")
.attr("r", )
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; }); nodeUpdate.select("text")
.style("fill-opacity", ); // Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + source.x + "," + source.y + ")"; })
.remove(); nodeExit.select("circle")
.attr("r", 1e-); nodeExit.select("text")
.style("fill-opacity", 1e-); // Update the links…
var link = svg.selectAll("path.link")
.data(links, function(d) { return d.target.id; }); // Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
}); // Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal)
.attr("class", function(d){
if(d.source.deal != null && d.source.deal != undefined){
if(d.target.deal != null && d.target.deal != undefined){
return "link link2";
}
}
return "link";
}); // Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
})
.remove(); // Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
} function getNode(){
var mynodes = null;
$.ajax({
url : "./node",
async : false, // 注意此处需要同步,因为返回完数据后,下面才能让结果的第一条selected
type : "POST",
dataType : "json",
success : function(data) {
mynodes = data;
console.log(mynodes);
//nodes = JSON.parse(nodes);
}
});
return mynodes;
} // Toggle children on click.
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else if(d._children){
d.children = d._children;
d._children = null;
}else {
var mnodes = getNode();
d.children = mnodes.children;
}
update(d);
} </script>

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

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

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

 /**
* @author "shihuc"
* @date 2016年11月14日
*/
package com.tk.es.search.controller; import java.util.ArrayList;
import java.util.HashMap; import javax.servlet.http.HttpServletRequest; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; import com.google.gson.Gson; /**
* @author chengsh05
*
*/
@Controller
public class D3Controller { @RequestMapping(value = "/d3")
public String d3Page(HttpServletRequest req){
return "d3demo2";
} @RequestMapping(value = "/node")
@ResponseBody
public String asyncGet(HttpServletRequest req){
HashMap<String, Object> data = new HashMap<String, Object>();
ArrayList<Object>elem1 = new ArrayList<Object>();
HashMap<String, String> elem1e = new HashMap<String, String>();
elem1e.put("name", "one");
elem1e.put("deal", "2");
HashMap<String, String> elem2e = new HashMap<String, String>();
elem2e.put("name", "two");
HashMap<String, String> elem3e = new HashMap<String, String>();
elem3e.put("name", "three");
elem1.add(elem1e);
elem1.add(elem2e);
elem1.add(elem3e); data.put("name", "Pause");
data.put("children", elem1); Gson gson = new Gson();
return gson.toJson(data);
}
}

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

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

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

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

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