2017-01-03 7 views
1

Je suis très nouveau à D3.js. Veuillez noter que la version 4 est utilisée.D3.js Process Diagram

fichier .HTML

(beaucoup dérivé de 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

sortie souhaitée:

enter image description here

La police est pas tout à fait la même, mais j'espère que idée est claire: essentiellement, je veux créer des flèches et pour que le fichier 4 apparaisse comme ci-dessus. Il se peut que je formate incorrectement le JSON; Si tel est le cas, faites-le moi savoir s'il est préférable de le mettre en forme.

En général, chaque (premier niveau) child doit pointer vers la droite, chaque subprocess doit pointer vers le bas, et tout child contenu dans un subprocess doit pointer vers le bas aussi bien.

+0

Qu'advient-il s'il y a des sous-processus multiples? – approxiblue

+0

@approxiblue Excellente question. Il n'y a qu'un seul sous-processus par enfant de premier niveau, au maximum. – Clarinetist

Répondre

2

NOUVELLE RÉPONSE

En pensant à cette dernière nuit et j'ai décidé mon approche naïve est à la force brute et hackish. Le problème est qu'en introduisant un deuxième tableau d'enfants (le tableau de sous-processus) et en plaçant ces nœuds directement, vous perdez la magie de d3.tree. Vous perdez sa capacité à marcher la relation parent/enfant et de déterminer la structure du lien. Au lieu de cela, je vous propose de conserver le seul tableau d'enfants et de marquer ceux-ci comme un sous-processus avec une propriété supplémentaire. La structure de données ressemblerait à ceci:

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

Ensuite, vous prétraiter les données pour déterminer à quelle profondeur le noeud est sous-processus; de la même manière, d3.tree marque des nœuds à des profondeurs spécifiques.

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); 
    } 
    }) 
} 

Après ce traitement, alors vous pouvez « fixer » les coordonnées du d3.tree génère pour créer la structure à angle droit:

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; 
    } 
    }); 

Le dessin utilise alors que le même code que vous aviez avant. Voici un exemple en cours d'exécution:

<!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>

RÉPONSE VIEUX

L'approche naïve est de vérifier chaque nœud entrant pour voir si elle a un sous-processus, puis ajouter les éléments supplémentaires à ce temps:

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; 
     }); 
    } 
}); 

Les têtes de flèches peuvent être ajoutées en utilisant le approche de marqueur ventional montrée 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)"; 
    }); 

Voici le code complet de la course avec ces changements et d'autres pour mettre tous ensemble:

<!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

J'ai un double, bien que je sois nouveau mais quand je clique sur tous les fichiers générés et puis je clique sur 'fichier 2' alors' fichier 4' est ajouté au 'fichier 5' et avec' fichier 3' synchrone –

+1

@pritishvaidya, bonne prise, merci. C'est maintenant réparé. – Mark

+0

Merci pour cette réponse. Je n'ai plus qu'une question: j'ai essayé d'insérer un 'children' dans un' subprocess' et il n'apparaît pas. Peut-être que j'ai formaté le JSON de manière incorrecte? 'Var treedata = { "name": "Fichier 2", "enfants": [{ "name": "Fichier 3", "subprocess": [{ "name": "Fichier 4", \t \t \t "enfants": [{ \t \t \t \t "name": "fichier 6" \t \t \t}] }], "enfants": [{ "name": "fichier 5" } ] },] }; ' – Clarinetist