2017-08-07 56 views
-1

我是D3 js的新手。我想用d3繪製一個樹形結構。但是,我希望節點之間的路徑不像通常的對角線和投影方法那樣靈活。我需要的節點之間的連接是這樣的:在d3樹路徑中創建靈活鏈接

enter image description here

如何在D3這樣做呢?

這是我現在使用對角線的代碼。

<html> 
 
<head> 
 
<title> Box office </title> 
 
</head> 
 
<body> 
 
<style> 
 
.node { 
 
\t \t cursor: pointer; 
 
\t } 
 

 
\t .node circle { 
 
\t fill: #fff; 
 
\t stroke: steelblue; 
 
\t stroke-width: 3px; 
 
\t } 
 

 
\t .node text { 
 
\t font: 12px sans-serif; 
 
\t } 
 

 
\t .link { 
 
\t fill: none; 
 
\t stroke: #ccc; 
 
\t stroke-width: 2px; 
 
\t } 
 

 
</style> 
 
<!-- load the d3.js library --> \t 
 
<script src="https://d3js.org/d3.v3.min.js"></script> 
 

 
<div id = "boxoffice"></div> 
 

 
<script type="text/javascript"> 
 

 

 

 
var sampleData = [ 
 
    { 
 
    "ChangeFlowsFromParent": "false", 
 
    "ChangeFlowsToParent": "false", 
 
    "StreamType": "Mainline", 
 
    "streamName": "ArgOS_2_0", 
 
    "Parent": "none", 
 
    "Compliance": "Released", 
 
    "children": [ 
 
     { 
 
     "ChangeFlowsFromParent": "true", 
 
     "ChangeFlowsToParent": "true", 
 
     "StreamType": "Development", 
 
     "streamName": "ArgOS_2_0_DHAL", 
 
     "Parent": "ArgOS_2_0", 
 
     "Compliance": "Released" 
 
     }, 
 
\t { 
 
     "ChangeFlowsFromParent": "true", 
 
     "ChangeFlowsToParent": "true", 
 
     "StreamType": "Development", 
 
     "streamName": "ArgOS_2_0_Dev", 
 
     "Parent": "ArgOS_2_0", 
 
     "Compliance": "Released", 
 
     "children": [ 
 
      { 
 
      "ChangeFlowsFromParent": "true", 
 
      "ChangeFlowsToParent": "true", 
 
      "StreamType": "Release", 
 
      "streamName": "r_ArgOS_0230", 
 
      "Parent": "ArgOS_2_0_Dev", 
 
      "Compliance": "Released" 
 
      }, 
 
\t \t { 
 
      "ChangeFlowsFromParent": "true", 
 
      "ChangeFlowsToParent": "true", 
 
      "StreamType": "Release", 
 
      "streamName": "branch1", 
 
      "Parent": "ArgOS_2_0_Dev", 
 
      "Compliance": "Released", 
 
\t \t \t "children": [ 
 
\t \t \t { 
 
\t \t \t \t "ChangeFlowsFromParent": "true", 
 
\t \t \t \t "ChangeFlowsToParent": "true", 
 
\t \t \t \t "StreamType": "Release", 
 
\t \t \t \t "streamName": "branch100", 
 
\t \t \t \t "Parent": "branch1", 
 
\t \t \t \t "Compliance": "Released" 
 
\t \t \t }, 
 
\t \t \t { 
 
\t \t \t \t "ChangeFlowsFromParent": "true", 
 
\t \t \t \t "ChangeFlowsToParent": "true", 
 
\t \t \t \t "StreamType": "Release", 
 
\t \t \t \t "streamName": "branch200", 
 
\t \t \t \t "Parent": "branch1", 
 
\t \t \t \t "Compliance": "Released", 
 
\t \t \t \t "children": [ 
 
\t \t \t \t { 
 
\t \t \t \t \t "ChangeFlowsFromParent": "true", 
 
\t \t \t \t \t "ChangeFlowsToParent": "true", 
 
\t \t \t \t \t "StreamType": "Release", 
 
\t \t \t \t \t "streamName": "honey", 
 
\t \t \t \t \t "Parent": "branch200", 
 
\t \t \t \t \t "Compliance": "Released" 
 
\t \t \t \t } 
 
\t \t \t \t ] 
 
\t \t \t } 
 
\t \t \t ] 
 
      } 
 
     ] 
 
     }, 
 
     { 
 
     "ChangeFlowsFromParent": "true", 
 
     "ChangeFlowsToParent": "true", 
 
     "StreamType": "Development", 
 
     "streamName": "ArgOS_2_0_IPC-Tracer", 
 
     "Parent": "ArgOS_2_0", 
 
     "Compliance": "Released", 
 
\t \t \t "children": [ 
 
\t \t \t { 
 
\t \t \t \t "ChangeFlowsFromParent": "true", 
 
\t \t \t \t "ChangeFlowsToParent": "true", 
 
\t \t \t \t "StreamType": "Release", 
 
\t \t \t \t "streamName": "ArgOS_2_0_IPC_child 1", 
 
\t \t \t \t "Parent": "ArgOS_2_0_IPC", 
 
\t \t \t \t "Compliance": "Released" 
 
\t \t \t }, 
 
\t \t \t { 
 
\t \t \t \t "ChangeFlowsFromParent": "true", 
 
\t \t \t \t "ChangeFlowsToParent": "true", 
 
\t \t \t \t "StreamType": "Release", 
 
\t \t \t \t "streamName": "ArgOS_2_0_IPC_child 2", 
 
\t \t \t \t "Parent": "ArgOS_2_0_IPC", 
 
\t \t \t \t "Compliance": "Released" 
 
\t \t \t } 
 
\t \t \t ] 
 
     }, 
 
     { 
 
     "ChangeFlowsFromParent": "true", 
 
     "ChangeFlowsToParent": "true", 
 
     "StreamType": "Development", 
 
     "streamName": "ArgOS_2_0_NSW_Temp", 
 
     "Parent": "ArgOS_2_0", 
 
     "Compliance": "Released" 
 
     }, 
 
     { 
 
     "ChangeFlowsFromParent": "true", 
 
     "ChangeFlowsToParent": "true", 
 
     "StreamType": "Development", 
 
     "streamName": "ArgOS_2_0_Test", 
 
     "Parent": "ArgOS_2_0", 
 
     "Compliance": "Released" 
 
     }, 
 
     { 
 
     "ChangeFlowsFromParent": "true", 
 
     "ChangeFlowsToParent": "true", 
 
     "StreamType": "Development", 
 
     "streamName": "ArgOS_2_CBD", 
 
     "Parent": "ArgOS_2_0", 
 
     "Compliance": "Released" 
 
     }, 
 
     { 
 
     "ChangeFlowsFromParent": "true", 
 
     "ChangeFlowsToParent": "true", 
 
     "StreamType": "Development", 
 
     "streamName": "test_mergewp", 
 
     "Parent": "ArgOS_2_0", 
 
     "Compliance": "Released" 
 
     } 
 
    ] 
 
    } 
 
] 
 

 

 
var margin = {top:100, bottom: 100, left:100, right:100}, 
 
width = 1800 - margin.left-margin.right, //total width minus side margins 
 
height = 1500 - margin.top - margin.bottom; //total height minus vertical margins 
 

 
var tree = d3.layout.tree().size([width, height]); 
 

 
tree.nodeSize([40,100]); 
 

 
var diagonal = d3.svg.diagonal() 
 
\t .projection(function(d) { return [d.x, -d.y]; }); 
 

 
\t 
 

 
var svgContainer = d3.select("#boxoffice").append("svg").attr("width", width).attr("height", height).append("g").attr("transform", "translate(" + (width/2) + "," + (height - 500) + ")"); 
 

 
var root = sampleData[0]; 
 
root.x0 = width/2; 
 
root.y0 = 0; 
 

 
update(root); 
 

 
d3.select(self.frameElement).style("height", "500px"); 
 

 
function update(sourceNode){ 
 

 
var nodes = tree.nodes(sourceNode).reverse(), 
 
links = tree.links(nodes); 
 

 
    
 
nodes.forEach(function(d){d.y = d.depth * 180}); 
 
    
 

 
var node = svgContainer.selectAll("g.node").attr("class", "node").data(nodes, function(d, i){return d.id || (d.id = ++i); }); 
 

 
var nodeEnter = node.enter().append("g") 
 
\t \t \t \t \t \t \t .attr("transform", function(d) { 
 
\t \t \t \t \t \t \t 
 
\t \t \t \t \t \t \t <!-- if(d.StreamType == "Mainline") --> 
 
\t \t \t \t \t \t \t \t <!-- return "translate(-100,-100)"; --> 
 
\t \t \t \t \t \t \t <!-- else --> 
 
\t \t \t \t \t \t \t \t return "translate(" + sourceNode.y0 + "," + sourceNode.x0 + ")"; 
 
\t \t \t \t \t \t \t \t \t \t \t \t \t \t 
 
\t \t \t \t \t \t \t }); 
 
    
 
nodeEnter.append("circle") 
 
\t \t .attr("r", 15) 
 
\t \t .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; }); 
 
\t \t 
 
\t \t nodeEnter.append("text") 
 
\t .attr("x", function(d) { return d.children || d._children ? -13 : 13; }) 
 
\t .attr("dy", ".35em") 
 
\t .attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; }) 
 
\t .text(function(d) { return d.streamName; }) 
 
\t .style("fill-opacity", 1e-6); 
 
\t 
 
\t \t \t 
 
    var nodeUpdate = node.transition() 
 
\t .duration(100) 
 
\t .attr("transform", function(d) { 
 
\t 
 
\t if(d.StreamType == "Mainline") 
 
\t  { 
 
\t \t \t var lastElement = nodes[0]; 
 
\t \t \t 
 
\t \t return "translate(" + (lastElement.y + 100) + "," + (lastElement.x -130) + ")"; //adding the root element 
 
\t \t } 
 
\t  else 
 
\t \t \t return "translate(" + d.y + "," + (d.x-(margin.top + margin.bottom)) + ")"; 
 
      
 
\t }); 
 

 
    nodeUpdate.select("circle") 
 
\t .attr("r", 10) 
 
\t .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; }); 
 

 
    nodeUpdate.select("text") 
 
\t .style("fill-opacity", 1); 
 

 
    var link = svgContainer.selectAll("path.link") 
 
\t .data(links, function(d) { return d.target.id; }); 
 
\t 
 
    link.enter().insert("path", "g") 
 
    .attr("class", "link") 
 
    .attr("d", function(d) { 
 
\t var o = {x: sourceNode.x0, y: sourceNode.y0}; 
 
\t return diagonal({source: o, target: o}); 
 
    }); 
 

 
    // Transition links to their new position. 
 
    link.transition() 
 
\t .duration(10) 
 
\t .attr("d", diagonal); 
 
\t \t 
 
\t 
 
nodes.forEach(function(d) { 
 
\t d.x0 = d.x; 
 
\t d.y0 = d.y; 
 
    }); 
 
\t 
 

 
} 
 

 
function positionLink(d) { 
 
    return "M" + d[0].x + "," + d[0].y 
 
     + "S" + d[1].x + "," + d[1].y 
 
     + " " + d[2].x + "," + d[2].y; 
 
} 
 

 
</script> 
 

 
</body> 
 
</html>

本規範的鏈接都是凌亂。我可以整理對角線路徑鏈接,但我希望我的鏈接具有動態性和靈活性。我怎樣才能做到這一點?

回答

0

目前尚不清楚「靈活」是什麼意思。更準確的術語可以從https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths#Bezier_Curves收集。例如,您可能需要一個三次貝塞爾曲線。

要實現這一點,你需要改變這部分代碼:

.attr("d", function(d) { 
var o = {x: sourceNode.x0, y: sourceNode.y0}; 
return diagonal({source: o, target: o}); 

更多信息可以在這個例子中,這似乎是實現你想要什麼發現: https://bl.ocks.org/mbostock/4339184

它使用:

.attr("d", d3.linkHorizontal() 
     .x(function(d) { return d.y; }) 
     .y(function(d) { return d.x; })); 

鏈接功能是相關的,但不等同於對角線功能。

+0

靈活,我的意思是鏈接需要像圖片中的線條(沒有對角線,沒有投影,沒有直線 - 到處都是我想要的曲線)。我嘗試使用d3.linkHorizo​​ntal() - 我得到腳本錯誤,說d3.linkHorizo​​ntal()不是一個函數。互聯網上也沒有太多的幫助。 – Ponni

+0

linkHorizo​​ntal附帶更新版本的d3 v4(https://github.com/d3/d3/releases/tag/v4.9.0) –

+0

如果我參考版本4,它說d3沒有定義。 – Ponni