2017-01-03 62 views
1

我對D3.js很新。請注意,正在使用版本4。D3.js流程圖

.HTML文件

(從here衍生很多):

<!DOCTYPE html> 
<meta charset="UTF-8"> 
<style> 

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

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

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

</style> 

<body> 

<!-- load the d3.js library --> 
<script src="d3.js"></script> 
<script> 

var treeData = 
    { 
    "name": "File 1", 
    "children": [ 
     { 
     "name": "File 2", 
     "children": [ 
      { "name": "File 3", 
      "subprocess": [ 
       {"name": "File 4"}], 
      "children" : [ 
       { "name": "File 5" } ] 

      }, 
      ] 
     } 
    ] 
    }; 

// Set the dimensions and margins of the diagram 
var margin = {top: 0, right: 90, bottom: 30, left: 25}, 
    width = 800 - margin.left - margin.right, 
    height = 200 - margin.top - margin.bottom; 

// append the svg object to the body of the page 
// appends a 'group' element to 'svg' 
// moves the 'group' element to the top left margin 
var svg = d3.select("body").append("svg") 
    .attr("width", width + margin.right + margin.left) 
    .attr("height", height + margin.top + margin.bottom) 
    .append("g") 
    .attr("transform", "translate(" 
      + margin.left + "," + margin.top + ")"); 

var i = 0, 
    duration = 750, // time between transitions (interaction) 
    root; 

// declares a tree layout and assigns the size 
var treemap = d3.tree().size([height, width]); 

// Assigns parent, children, height, depth 
root = d3.hierarchy(treeData, function(d) { return d.children; }); 
root.x0 = 0; 
root.y0 = 0; 
console.log(root); 

// Collapse after the second level 
root.children.forEach(collapse); 

update(root); 

// Collapse the node and all it's children 
function collapse(d) { 
    if(d.children) { 
    d._children = d.children 
    d._children.forEach(collapse) 
    d.children = null 
    } 
} 

function update(source) { 

    // Assigns the x and y position for the nodes 
    var treeData = treemap(root); 

    console.log(treeData.descendants()); 

    // Compute the new tree layout. 
    var nodes = treeData.descendants(), 
     links = treeData.descendants().slice(1); 

    // Normalize for fixed-depth. 
    nodes.forEach(function(d){ d.y = d.depth * 180}); 

    // ****************** Nodes section *************************** 

    // Update the nodes... 
    var node = svg.selectAll('g.node') 
     .data(nodes, function(d) {return d.id || (d.id = ++i); }); 

    // Enter any new modes at the parent's previous position. 
    var nodeEnter = node.enter().append('g') 
     .attr('class', 'node') 
     .attr("transform", function(d) { 
     return "translate(" + source.y0 + "," + source.x0 + ")"; 
    }) 
    .on('click', click); 

    // Add Circle for the nodes 
    nodeEnter.append('circle') 
     .attr('class', 'node') 
     .attr('r', 1e-6) 
     .style("fill", function(d) { 
      return d._children ? "lightsteelblue" : "#fff"; 
     }); 

    // Add labels for the nodes 
    nodeEnter.append('text') 
     .attr("dy", "2em") 
     .attr("x", function(d) { 
      return d.children || d._children ? 13 : 13; 
     }) 
     .attr("text-anchor", function(d) { 
      return d.children || d._children ? "start" : "start"; 
     }) 
     .text(function(d) { return d.data.name; }); 

    // UPDATE 
    var nodeUpdate = nodeEnter.merge(node); 

    // Transition to the proper position for the node 
    nodeUpdate.transition() 
    .duration(duration) 
    .attr("transform", function(d) { 
     return "translate(" + d.y + "," + d.x + ")"; 
    }); 

    // Update the node attributes and style 
    nodeUpdate.select('circle.node') 
    .attr('r', 10) 
    .style("fill", function(d) { 
     return d._children ? "lightsteelblue" : "#fff"; 
    }) 
    .attr('cursor', 'pointer'); 


    // Remove any exiting nodes 
    var nodeExit = node.exit().transition() 
     .duration(duration) 
     .attr("transform", function(d) { 
      return "translate(" + source.y + "," + source.x + ")"; 
     }) 
     .remove(); 

    // On exit reduce the node circles size to 0 
    nodeExit.select('circle') 
    .attr('r', 1e-6); 

    // On exit reduce the opacity of text labels 
    nodeExit.select('text') 
    .style('fill-opacity', 1e-6); 

    // ****************** links section *************************** 

    // Update the links... 
    var link = svg.selectAll('path.link') 
     .data(links, function(d) { return d.id; }); 

    // Enter any new links at the parent's previous position. 
    var linkEnter = link.enter().insert('path', "g") 
     .attr("class", "link") 
     .attr('d', function(d){ 
     var o = {x: source.x0, y: source.y0} 
     return diagonal(o, o) 
     }); 

    // UPDATE 
    var linkUpdate = linkEnter.merge(link); 

    // Transition back to the parent element position 
    linkUpdate.transition() 
     .duration(duration) 
     .attr('d', function(d){ return diagonal(d, d.parent) }); 

    // Remove any exiting links 
    var linkExit = link.exit().transition() 
     .duration(duration) 
     .attr('d', function(d) { 
     var o = {x: source.x, y: source.y} 
     return diagonal(o, o) 
     }) 
     .remove(); 

    // Store the old positions for transition. 
    nodes.forEach(function(d){ 
    d.x0 = d.x; 
    d.y0 = d.y; 
    }); 

    // Creates a curved (diagonal) path from parent to the child nodes 
    function diagonal(s, d) { 

    path = `M ${s.y} ${s.x} 
      L ${d.y} ${d.x}`; 

    return path; 
    } 

    // Toggle children on click. 
    function click(d) { 
    if (d.children) { 
     d._children = d.children; 
     d.children = null; 
     } else { 
     d.children = d._children; 
     d._children = null; 
     } 
    update(d); 
    } 
} 

</script> 
</body> 

enter image description here

希望的輸出:

enter image description here

的字體是不太同樣,但我希望是想法很清楚:實質上,我想要創建箭頭,並將File 4顯示爲上述內容。我可能會錯誤地格式化JSON;如果是這種情況,請讓我知道它應該是最好的格式。

一般來說,每個(第一級)child應指向右側,每個subprocess應該向下指向,並且包含在一個subprocess任何child應指向向下爲好。

+0

發生什麼情況,如果有多個子進程? – approxiblue

+0

@approxiblue優秀的問題。至多每個一級孩子只有一個子進程。 – Clarinetist

回答

2

新的答案

思考這個昨晚,我決定我的幼稚的做法是hackish的和蠻力。問題是通過引入第二個子數組(子進程數組)並直接放置這些節點,您將失去d3.tree的魔力。你失去了散步父母/子女關係並確定鏈接結構的能力。相反,我建議您保留單個數組的子項並將它們標記爲具有附加屬性的子流程。數據結構如下所示:

var treeData = { 
    "name": "File 1", 
    "children": [{ 
    "name": "File 2", 
    "children": [{ 
     "name": "File 3", 
     "children": [{ 
     "name": "File 5" 
     }, { 
     "name": "File 4", 
     "type": "subprocess", //<-- this is a subprocess with children 
     "children": [{ 
      "name": "File 6" 
     }] 
     }] 
    }] 
    }] 
}; 

然後您預處理數據以確定子進程節點在哪個深度;在很多方面d3.tree標記在特定深度的節點。

root.data['depthToSub'] = 0; 
isChildOfProcess(root.children, 0); 

// Collapse after the second level 
root.children.forEach(collapse); 

function isChildOfProcess(children, depth) { 
    children.forEach(function(d) { 
    d.data['depthToSub'] = depth; 
    if (d.data.type && d.data.type === "subprocess") { 
     d.data['depthToSub'] = 1; 
     isChildOfProcess(d.children, 2); 
    } else if (depth > 0 && d.children) { 
     isChildOfProcess(d.children, ++depth); 
    } else if (d.children) { 
     isChildOfProcess(d.children, 0); 
    } 
    }) 
} 

此處理,那麼你可以「修復」後的座標d3.tree生成以創建直角結構:

nodes.forEach(function(d) { 

    d.y = d.depth * 180; 

    if (d.parent) { 
     d.x = d.parent.x; 
    } 

    if (d.data.depthToSub) { 
     d.y = d.parent.y; 
     d.x = (d.data.depthToSub * nodes[0].x) + nodes[0].x; 
    } 
    }); 

繪圖然後只需使用您之前所擁有的相同的代碼。下面是一個運行示例:

<!DOCTYPE html> 
 
<html> 
 

 
<head> 
 
    <meta charset="UTF-8" /> 
 
    <script data-require="[email protected]" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script> 
 
    <style> 
 
    .node circle { 
 
     fill: #fff; 
 
     stroke: steelblue; 
 
     stroke-width: 3px; 
 
    } 
 
    .node text { 
 
     font: 12px sans-serif; 
 
    } 
 
    .link { 
 
     fill: none; 
 
     stroke: #ccc; 
 
     stroke-width: 2px; 
 
    } 
 
    .arrow { 
 
     fill: none; 
 
     stroke: #ccc; 
 
     stroke-width: 1px; 
 
    } 
 
    </style> 
 
</head> 
 

 
<body> 
 
    <script> 
 
    var treeData = { 
 
     "name": "File 1", 
 
     "children": [{ 
 
     "name": "File 2", 
 
     "children": [{ 
 
      "name": "File 3", 
 
      "children": [{ 
 
      "name": "File 5" 
 
      }, { 
 
      "name": "File 4", 
 
      "type": "subprocess", 
 
      "children": [{ 
 
       "name": "File 6", 
 
       "children": [{ 
 
       "name": "File 7" 
 
       }] 
 
      }] 
 
      }] 
 
     }] 
 
     }] 
 
    }; 
 

 
    // Set the dimensions and margins of the diagram 
 
    var margin = { 
 
     top: 0, 
 
     right: 90, 
 
     bottom: 30, 
 
     left: 25 
 
     }, 
 
     width = 800 - margin.left - margin.right, 
 
     height = 800 - margin.top - margin.bottom; 
 

 
    // append the svg object to the body of the page 
 
    // appends a 'group' element to 'svg' 
 
    // moves the 'group' element to the top left margin 
 
    var svg = d3.select("body").append("svg") 
 
     .attr("width", width + margin.right + margin.left) 
 
     .attr("height", height + margin.top + margin.bottom) 
 
     .append("g") 
 
     .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 
 

 
    var defs = svg.append("defs"); 
 

 
    defs 
 
     .append("marker") 
 
     .attr("id", "arrow") 
 
     .attr("class", "arrow") 
 
     .attr("viewBox", "0 -4 10 10") 
 
     .attr("refX", 12) 
 
     .attr("refY", 0) 
 
     .attr("markerWidth", 12) 
 
     .attr("markerHeight", 12) 
 
     .attr("orient", "auto") 
 
     .append("path") 
 
     .attr("d", "M0,-4L8,0L0,4"); 
 

 
    var i = 0, 
 
     duration = 750, // time between transitions (interaction) 
 
     root; 
 

 
    // declares a tree layout and assigns the size 
 
    var treemap = d3.tree().size([200, width]); 
 

 
    // Assigns parent, children, height, depth 
 
    root = d3.hierarchy(treeData, function(d) { 
 
     return d.children; 
 
    }); 
 
    root.x0 = 0; 
 
    root.y0 = 0; 
 

 
    root.data['depthToSub'] = 0; 
 
    isChildOfProcess(root.children, 0); 
 

 
    // Collapse after the second level 
 
    root.children.forEach(collapse); 
 

 
    function isChildOfProcess(children, depth) { 
 
     children.forEach(function(d) { 
 
     d.data['depthToSub'] = depth; 
 
     if (d.data.type && d.data.type === "subprocess") { 
 
      d.data['depthToSub'] = 1; 
 
      isChildOfProcess(d.children, 2); 
 
     } else if (depth > 0 && d.children) { 
 
      isChildOfProcess(d.children, ++depth); 
 
     } else if (d.children) { 
 
      isChildOfProcess(d.children, 0); 
 
     } 
 
     }) 
 
    } 
 

 
    // Collapse after the second level 
 
    root.children.forEach(collapse); 
 

 
    update(root); 
 

 
    // Collapse the node and all it's children 
 
    function collapse(d) { 
 
     if (d.children) { 
 
     d._children = d.children 
 
     d._children.forEach(collapse) 
 
     d.children = null 
 
     } 
 
    } 
 

 
    function update(source) { 
 

 
     // Assigns the x and y position for the nodes 
 
     var treeData = treemap(root); 
 

 
     // Compute the new tree layout. 
 
     var nodes = treeData.descendants(), 
 
     links = treeData.descendants().slice(1); 
 

 
     nodes.forEach(function(d) { 
 

 
     d.y = d.depth * 180; 
 

 
     if (d.parent) { 
 
      d.x = d.parent.x; 
 
     } 
 

 
     if (d.data.depthToSub) { 
 
      d.y = d.parent.y; 
 
      d.x = (d.data.depthToSub * nodes[0].x) + nodes[0].x; 
 
     } 
 
     }); 
 

 
     // ****************** Nodes section *************************** 
 

 
     // Update the nodes... 
 
     var node = svg.selectAll('g.node') 
 
     .data(nodes, function(d) { 
 
      return d.id || (d.id = ++i); 
 
     }); 
 

 
     // Enter any new modes at the parent's previous position. 
 
     var nodeEnter = node.enter().append('g') 
 
     .attr('class', 'node') 
 
     .attr("transform", function(d) { 
 
      return "translate(" + source.y0 + "," + source.x0 + ")"; 
 
     }) 
 
     .on('click', click); 
 

 
     // Add Circle for the nodes 
 
     nodeEnter.append('circle') 
 
     .attr('class', 'node') 
 
     .attr('r', 1e-6) 
 
     .style("fill", function(d) { 
 
      return d._children ? "lightsteelblue" : "#fff"; 
 
     }); 
 

 
     // Add labels for the nodes 
 
     nodeEnter.append('text') 
 
     .attr("dy", "2em") 
 
     .attr("x", function(d) { 
 
      return d.children || d._children ? 13 : 13; 
 
     }) 
 
     .attr("text-anchor", function(d) { 
 
      return d.children || d._children ? "start" : "start"; 
 
     }) 
 
     .text(function(d) { 
 
      return d.data.name; 
 
     }); 
 

 
     // UPDATE 
 
     var nodeUpdate = nodeEnter.merge(node); 
 

 
     // Transition to the proper position for the node 
 
     nodeUpdate.transition() 
 
     .duration(duration) 
 
     .attr("transform", function(d) { 
 
      return "translate(" + d.y + "," + d.x + ")"; 
 
     }); 
 

 
     // Update the node attributes and style 
 
     nodeUpdate.select('circle.node') 
 
     .attr('r', 10) 
 
     .style("fill", function(d) { 
 
      return d._children ? "lightsteelblue" : "#fff"; 
 
     }) 
 
     .attr('cursor', 'pointer'); 
 

 

 
     // Remove any exiting nodes 
 
     var nodeExit = node.exit().transition() 
 
     .duration(duration) 
 
     .attr("transform", function(d) { 
 
      return "translate(" + source.y + "," + source.x + ")"; 
 
     }) 
 
     .remove(); 
 

 
     // On exit reduce the node circles size to 0 
 
     nodeExit.select('circle') 
 
     .attr('r', 1e-6); 
 

 
     // On exit reduce the opacity of text labels 
 
     nodeExit.select('text') 
 
     .style('fill-opacity', 1e-6); 
 

 
     // ****************** links section *************************** 
 

 
     // Update the links... 
 
     var link = svg.selectAll('path.link') 
 
     .data(links, function(d) { 
 
      return d.id; 
 
     }); 
 

 
     // Enter any new links at the parent's previous position. 
 
     var linkEnter = link.enter().insert('path', "g") 
 
     .attr("class", "link") 
 
     .attr('d', function(d) { 
 
      var o = { 
 
      x: source.x0, 
 
      y: source.y0 
 
      } 
 
      return diagonal(o, o) 
 
     }) 
 
     .attr("marker-end", function(d) { 
 
      return "url(#arrow)"; 
 
     }); 
 

 
     // UPDATE 
 
     var linkUpdate = linkEnter.merge(link); 
 

 
     // Transition back to the parent element position 
 
     linkUpdate.transition() 
 
     .duration(duration) 
 
     .attr('d', function(d) { 
 
      return diagonal(d, d.parent) 
 
     }); 
 

 
     // Remove any exiting links 
 
     var linkExit = link.exit().transition() 
 
     .duration(duration) 
 
     .attr('d', function(d) { 
 
      var o = { 
 
      x: source.x, 
 
      y: source.y 
 
      } 
 
      return diagonal(o, o) 
 
     }) 
 
     .remove(); 
 

 
     // Store the old positions for transition. 
 
     nodes.forEach(function(d) { 
 
     d.x0 = d.x; 
 
     d.y0 = d.y; 
 
     }); 
 

 
     // Creates a curved (diagonal) path from parent to the child nodes 
 
     function diagonal(s, d) { 
 

 
     path = `M ${d.y} ${d.x} 
 
      L ${s.y} ${s.x}`; 
 

 
     return path; 
 
     } 
 

 
     // Toggle children on click. 
 
     function click(d) { 
 
     if (d.children) { 
 
      d._children = d.children; 
 
      d.children = null; 
 
     } else { 
 
      d.children = d._children; 
 
      d._children = null; 
 
     } 
 
     update(d); 
 
     } 
 
    } 
 
    </script> 
 
</body> 
 

 
</html>

OLD ANSWER

簡易方法是檢查每個進入節點,看它是否有一個子進程,然後在該添加其他元素時間:

nodeEnter.each(function(d){ 
    if (d.data.subprocess){ 
    var sp = d3.select(this).append('g') 
     .datum(d) 
     .attr('class','subprocess'); 

    sp.append('path') 
     .attr('d', 'M0,' + (-d.x - subProcessDist) + 'L0,0') 
     .style('stroke', '#ccc') 
     .style('stroke-width', '2px') 
     .attr("marker-end", function(d) { 
     return "url(#arrow)"; 
     }); 

    sp.append('circle') 
     .attr('r', 1e-6) 
     .style('fill', '#fff'); 

    sp.append('text') 
     .attr('x', 13) 
     .style('text-anchor', 'start') 
     .text(function(d){ 
     return d.data.subprocess[0].name; 
     }); 
    } 
}); 

箭頭可以使用con常規標記方法顯示here

var defs = svg.append("defs"); 

defs 
    .append("marker") 
    .attr("id", "arrow") 
    .attr("class", "arrow") 
    .attr("viewBox", "0 -4 10 10") 
    .attr("refX", 12) 
    .attr("refY", 0) 
    .attr("markerWidth", 12) 
    .attr("markerHeight", 12) 
    .attr("orient", "auto") 
    .append("path") 
    .attr("d", "M0,-4L8,0L0,4"); 

... 

// Enter any new links at the parent's previous position. 
var linkEnter = link.enter().insert('path', "g") 
    .attr("class", "link") 
    .attr('d', function(d) { 
    var o = { 
     x: source.x0, 
     y: source.y0 
    } 
    return diagonal(o, o) 
    }) 
    .attr("marker-end", function(d) { 
    return "url(#arrow)"; 
    }); 

這裏是完全跑步碼這些變化和其他人把它放在一起:

<!DOCTYPE html> 
 
<html> 
 

 
<head> 
 
    <meta charset="UTF-8" /> 
 
    <script data-require="[email protected]" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script> 
 
    <style> 
 
    .node circle { 
 
     fill: #fff; 
 
     stroke: steelblue; 
 
     stroke-width: 3px; 
 
    } 
 
    
 
    .node text { 
 
     font: 12px sans-serif; 
 
    } 
 
    
 
    .link { 
 
     fill: none; 
 
     stroke: #ccc; 
 
     stroke-width: 2px; 
 
    } 
 
    
 
    .arrow{ 
 
     fill: none; 
 
     stroke: #ccc; 
 
     stroke-width: 1px; 
 
    } 
 
    </style> 
 
</head> 
 

 
<body> 
 

 
    <script> 
 
    
 
    var subProcessDist = 15; 
 
    
 
    var treeData = { 
 
     "name": "File 1", 
 
     "children": [{ 
 
     "name": "File 2", 
 
     "children": [{ 
 
      "name": "File 3", 
 
      "subprocess": [{ 
 
      "name": "File 4" 
 
      }], 
 
      "children": [{ 
 
      "name": "File 5" 
 
      }] 
 

 
     }, ] 
 
     }] 
 
    }; 
 

 
    // Set the dimensions and margins of the diagram 
 
    var margin = { 
 
     top: 0, 
 
     right: 90, 
 
     bottom: 30, 
 
     left: 25 
 
     }, 
 
     width = 800 - margin.left - margin.right, 
 
     height = 200 - margin.top - margin.bottom; 
 

 
    // append the svg object to the body of the page 
 
    // appends a 'group' element to 'svg' 
 
    // moves the 'group' element to the top left margin 
 
    var svg = d3.select("body").append("svg") 
 
     .attr("width", width + margin.right + margin.left) 
 
     .attr("height", height + margin.top + margin.bottom) 
 
     .append("g") 
 
     .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 
 
     
 
    var defs = svg.append("defs"); 
 
    
 
    defs 
 
     .append("marker") 
 
     .attr("id", "arrow") 
 
     .attr("class", "arrow") 
 
     .attr("viewBox", "0 -4 10 10") 
 
     .attr("refX", 12) 
 
     .attr("refY", 0) 
 
     .attr("markerWidth", 12) 
 
     .attr("markerHeight", 12) 
 
     .attr("orient", "auto") 
 
     .append("path") 
 
     .attr("d", "M0,-4L8,0L0,4"); 
 
     
 
    var i = 0, 
 
     duration = 750, // time between transitions (interaction) 
 
     root; 
 

 
    // declares a tree layout and assigns the size 
 
    var treemap = d3.tree().size([height, width]); 
 

 
    // Assigns parent, children, height, depth 
 
    root = d3.hierarchy(treeData, function(d) { 
 
     return d.children; 
 
    }); 
 
    root.x0 = 0; 
 
    root.y0 = 0; 
 
    //console.log(root); 
 

 
    // Collapse after the second level 
 
    root.children.forEach(collapse); 
 

 
    update(root); 
 

 
    // Collapse the node and all it's children 
 
    function collapse(d) { 
 
     if (d.children) { 
 
     d._children = d.children 
 
     d._children.forEach(collapse) 
 
     d.children = null 
 
     } 
 
    } 
 

 
    function update(source) { 
 

 
     // Assigns the x and y position for the nodes 
 
     var treeData = treemap(root); 
 

 
     // Compute the new tree layout. 
 
     var nodes = treeData.descendants(), 
 
     links = treeData.descendants().slice(1); 
 

 
     // Normalize for fixed-depth. 
 
     nodes.forEach(function(d) { 
 
     d.y = d.depth * 180 
 
     }); 
 

 
     // ****************** Nodes section *************************** 
 

 
     // Update the nodes... 
 
     var node = svg.selectAll('g.node') 
 
     .data(nodes, function(d) { 
 
      return d.id || (d.id = ++i); 
 
     }); 
 

 
     // Enter any new modes at the parent's previous position. 
 
     var nodeEnter = node.enter().append('g') 
 
     .attr('class', 'node') 
 
     .attr("transform", function(d) { 
 
      return "translate(" + source.y0 + "," + source.x0 + ")"; 
 
     }) 
 
     .on('click', click); 
 
     
 
     nodeEnter.each(function(d){ 
 

 
      if (d.data.subprocess){ 
 
      var sp = d3.select(this).append('g') 
 
       .datum(d) 
 
       .attr('class','subprocess'); 
 
       
 
      sp.append('path') 
 
       .attr('d', 'M0,' + (-d.x - subProcessDist) + 'L0,0') 
 
       .style('stroke', '#ccc') 
 
       .style('stroke-width', '2px') 
 
       .attr("marker-end", function(d) { 
 
       return "url(#arrow)"; 
 
       }); 
 
       
 
      sp.append('circle') 
 
       .attr('r', 1e-6) 
 
       .style('fill', '#fff'); 
 
       
 
      sp.append('text') 
 
       .attr('x', 13) 
 
       .style('text-anchor', 'start') 
 
       .text(function(d){ 
 
       return d.data.subprocess[0].name; 
 
       }); 
 
      } 
 
     }); 
 

 
     // Add Circle for the nodes 
 
     nodeEnter.append('circle') 
 
     .attr('class', 'node') 
 
     .attr('r', 1e-6) 
 
     .style("fill", function(d) { 
 
      return d._children ? "lightsteelblue" : "#fff"; 
 
     }); 
 

 
     // Add labels for the nodes 
 
     nodeEnter.append('text') 
 
     .attr("dy", "2em") 
 
     .attr("x", function(d) { 
 
      return d.children || d._children ? 13 : 13; 
 
     }) 
 
     .attr("text-anchor", function(d) { 
 
      return d.children || d._children ? "start" : "start"; 
 
     }) 
 
     .text(function(d) { 
 
      return d.data.name; 
 
     }); 
 

 
     // UPDATE 
 
     var nodeUpdate = nodeEnter.merge(node); 
 

 
     // Transition to the proper position for the node 
 
     nodeUpdate.transition() 
 
     .duration(duration) 
 
     .attr("transform", function(d) { 
 
      return "translate(" + d.y + "," + d.x + ")"; 
 
     }); 
 
     
 
     nodeUpdate.select('.subprocess') 
 
     .transition() 
 
     .duration(duration) 
 
     .attr("transform", function(d) { 
 
      return "translate(" + (0) + "," + (d.x + subProcessDist) + ")"; 
 
     }); 
 
     nodeUpdate.select('.subprocess').select('circle').attr('r', 10); 
 

 
     // Update the node attributes and style 
 
     nodeUpdate.select('circle.node') 
 
     .attr('r', 10) 
 
     .style("fill", function(d) { 
 
      return d._children ? "lightsteelblue" : "#fff"; 
 
     }) 
 
     .attr('cursor', 'pointer'); 
 

 

 
     // Remove any exiting nodes 
 
     var nodeExit = node.exit().transition() 
 
     .duration(duration) 
 
     .attr("transform", function(d) { 
 
      return "translate(" + source.y + "," + source.x + ")"; 
 
     }) 
 
     .remove(); 
 

 
     // On exit reduce the node circles size to 0 
 
     nodeExit.selectAll('circle') 
 
     .attr('r', 1e-6); 
 

 
     // On exit reduce the opacity of text labels 
 
     nodeExit.selectAll('text') 
 
     .style('fill-opacity', 1e-6); 
 

 
     // ****************** links section *************************** 
 

 
     // Update the links... 
 
     var link = svg.selectAll('path.link') 
 
     .data(links, function(d) { 
 
      return d.id; 
 
     }); 
 

 
     // Enter any new links at the parent's previous position. 
 
     var linkEnter = link.enter().insert('path', "g") 
 
     .attr("class", "link") 
 
     .attr('d', function(d) { 
 
      var o = { 
 
      x: source.x0, 
 
      y: source.y0 
 
      } 
 
      return diagonal(o, o) 
 
     }) 
 
     .attr("marker-end", function(d) { 
 
      return "url(#arrow)"; 
 
     }); 
 

 
     // UPDATE 
 
     var linkUpdate = linkEnter.merge(link); 
 

 
     // Transition back to the parent element position 
 
     linkUpdate.transition() 
 
     .duration(duration) 
 
     .attr('d', function(d) { 
 
      return diagonal(d, d.parent) 
 
     }); 
 

 
     // Remove any exiting links 
 
     var linkExit = link.exit().transition() 
 
     .duration(duration) 
 
     .attr('d', function(d) { 
 
      var o = { 
 
      x: source.x, 
 
      y: source.y 
 
      } 
 
      return diagonal(o, o) 
 
     }) 
 
     .remove(); 
 

 
     // Store the old positions for transition. 
 
     nodes.forEach(function(d) { 
 
     d.x0 = d.x; 
 
     d.y0 = d.y; 
 
     }); 
 

 
     // Creates a curved (diagonal) path from parent to the child nodes 
 
     function diagonal(s, d) { 
 

 
     path = `M ${d.y} ${d.x} 
 
      L ${s.y} ${s.x}`; 
 

 
     return path; 
 
     } 
 

 
     // Toggle children on click. 
 
     function click(d) { 
 
     if (d.children) { 
 
      d._children = d.children; 
 
      d.children = null; 
 
     } else { 
 
      d.children = d._children; 
 
      d._children = null; 
 
     } 
 
     update(d); 
 
     } 
 
    } 
 
    </script> 
 
</body> 
 

 
</html>

+1

我有一個雙,雖然我是新來的,但每當我點擊生成的所有文件,然後我點擊'文件2',然後'文件4'被追加到'文件5'和'文件3'同步 –

+1

@pritishvaidya,很好,趕上,謝謝。現在已修好。 – Mark

+0

謝謝你的回答。我只有一個問題:我試圖在'subprocess'中插入一個'children',它沒有顯示出來。也許我錯誤地格式化了JSON? 'VAR treeData = { 「名稱」: 「文件2」, 「孩子」:[{ 「名稱」: 「文件3」, 「子」:[{ 「名稱」: 「文件4」, \t \t \t 「孩子」:[{ \t \t \t \t 「姓名」: 「文件6」 \t \t \t}] }], 「孩子」:[{ 「姓名」: 「文件5」 } ] },] };' – Clarinetist