2016-07-11 13 views
5

Wenn Sie mit dem zusammenklappbaren Baum unten spielen, werden Sie sehen, dass wenn Sie das Ende des Baums erreichen und die Knoten erweitern und reduzieren, die Linien verrücktes Zeug machen und ich bin mir nicht ganz sicher, was das Verhalten antreibt oder ob mein Neuschreiben von komplett außerhalb der Basis ist. Ich baute eine flache Datenstruktur ab und verwendete stratify, um sie in ein Baumlayout umzuwandeln. Das einzige Problem bisher ist die Linienübergänge ... irgendwelche Gedanken?d3.js v4 verrückter Linkübergang in reduzierbarem Baum Beispiel

var data = [{ 
 
    "name": "Hazer 5000", 
 
    "parent": "CFO", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/stephen.jpg" 
 
    }, { 
 
    "name": "Employee 1", 
 
    "parent": "Hazer 5000", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/cory.jpg" 
 
    }, { 
 
    "name": "Analytics Area", 
 
    "parent": "Hazer 5000", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/matt.jpg" 
 
    }, { 
 
    "name": "Employee 2", 
 
    "parent": "Hazer 5000", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/XinheZhang.jpg" 
 
    }, { 
 
    "name": "Employee 3", 
 
    "parent": "Hazer 5000", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/craig.jpg" 
 
    }, { 
 
    "name": "Employee 4", 
 
    "parent": "Hazer 5000", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/youri.jpg" 
 
    }, { 
 
    "name": "Intern 1", 
 
    "parent": "Analytics Area", 
 
    "img": "" 
 
    }, { 
 
    "name": "Inter 2", 
 
    "parent": "Analytics Area", 
 
    "img": "" 
 
    }, { 
 
    "name": "CFO", 
 
    "parent": null, 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Brett.jpg" 
 
    }, { 
 
    "name": "CPA", 
 
    "parent": "CFO", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Wes.jpg" 
 
    }, { 
 
    "name": "Matt's wife", 
 
    "parent": "CPA", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Amy_R.jpg" 
 
    }, { 
 
    "name": "Employee 5", 
 
    "parent": "CPA", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/DavidBriley.jpg" 
 
    }, { 
 
    "name": "Employee 6", 
 
    "parent": "CPA", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/BrittanyAllred_.jpg" 
 
    }, { 
 
    "name": "Employee 7", 
 
    "parent": "CPA", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Shea.jpg" 
 
    }, { 
 
    "name": "Employee 8", 
 
    "parent": "Matt's wife", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Mindy.jpg" 
 
    }, { 
 
    "name": "Employee 9", 
 
    "parent": "Matt's wife", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Jessica_Stacy.jpg" 
 
    }, { 
 
    "name": "Employee 10", 
 
    "parent": "Matt's wife", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/FraleaneHudson.jpg" 
 
    },{ 
 
    "name": "Employee 11", 
 
    "parent": "Employee 9", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/MeganPierce_.jpg" 
 
    },{ 
 
    "name": "Intern 3", 
 
    "parent": "Employee 8", 
 
    "img": "" 
 
    }, { 
 
    "name": "Intern 4", 
 
    "parent": "Employee 8", 
 
    "img": "" 
 
    } 
 

 
]; 
 

 
var margin = {top: 20, right: 120, bottom: 20, left: 120}, 
 
    width = 960 - margin.right - margin.left, 
 
    height = 800 - margin.top - margin.bottom; 
 

 
var i = 0, 
 
    duration = 750, 
 
    root; 
 

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

 
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 stratify = d3.stratify() 
 
    .id(function(d) { 
 
    return d.name;//This position 
 
    }) 
 
    .parentId(function(d) { 
 
    return d.parent; //What position this position reports to 
 
    }); 
 

 
var root = stratify(data); 
 

 
root.each(function(d) { 
 
    
 
    d.name = d.id; //transferring name to a name variable 
 
    d.id = i; //Assigning numerical Ids 
 
    i++; 
 
    
 
    }); 
 

 
    root.x0 = height/2; 
 
    root.y0 = 0; 
 

 
    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(root).descendants(), 
 
     links = nodes.slice(1); 
 

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

 
    // 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.y0 + "," + source.x0 + ")"; }) 
 
     .on("click", click); 
 

 
    nodeEnter.append("circle") 
 
     .attr("r", 1e-6) 
 
     .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; }); 
 

 
    nodeEnter.append("text") 
 
     .attr("x", function(d) { return d.children || d._children ? -10 : 10; }) 
 
     .attr("dy", ".35em") 
 
     .attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; }) 
 
     .text(function(d) { return d.name; }) 
 
     .style("fill-opacity", 1e-6); 
 

 
    // Transition nodes to their new position. 
 
    var nodeUpdate = node.merge(nodeEnter).transition() 
 
     .duration(duration) 
 
     .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; }); 
 

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

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

 
    // Transition exiting nodes to the parent's new position. 
 
    var nodeExit = node.exit().transition() 
 
     .duration(duration) 
 
     .attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; }) 
 
     .remove(); 
 

 
    nodeExit.select("circle") 
 
     .attr("r", 1e-6); 
 

 
    nodeExit.select("text") 
 
     .style("fill-opacity", 1e-6); 
 

 

 
    // Update the links… 
 
    var link = svg.selectAll("path.link") 
 
     .data(links); 
 
    
 
    // Transition links to their new position. 
 
    link.transition() 
 
     .duration(duration) 
 
     .attr("d", connector); 
 

 
    // 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, parent:{x: source.x0, y: source.y0}}; 
 
     return connector(o); 
 
     }); 
 

 
    // Transition links to their new position. 
 
    link.merge(linkEnter).transition() 
 
     .duration(duration) 
 
     .attr("d", connector); 
 

 
    // 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, parent:{x: source.x, y: source.y}}; 
 
     return connector(o); 
 
     }) 
 
     .remove(); 
 

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

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

 
function connector(d) { 
 
    return "M" + d.y + "," + d.x + 
 
    "C" + (d.y + d.parent.y)/2 + "," + d.x + 
 
    " " + (d.y + d.parent.y)/2 + "," + d.parent.x + 
 
    " " + d.parent.y + "," + d.parent.x; 
 
}
.node { 
 
    cursor: pointer; 
 
} 
 

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

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

 
.link { 
 
    fill: none; 
 
    stroke: #ccc; 
 
    stroke-width: 1.5px;
<script src="https://d3js.org/d3.v4.min.js"></script>

Antwort

6

Sieht aus wie ein Problem mit den verwendeten Schlüssel für die Datenbindung des Links. Ich beziehe mich auf die Tatsache, dass, wenn Sie .data(links) ohne ein zweites Argument aufrufen, d3 i als Schlüssel verwendet, wenn es die enter/update/exit-Sets berechnet, und wenn Sie also erweitern/reduzieren der gleiche Link kann verschiedene i Werte haben.

Ich glaube, ich habe es funktioniert, und alles, was ich tat, war, dass zweite Param als eine Funktion, die eine eindeutige Link-ID durch die Kombination der ID des Knotens mit der ID des übergeordneten Knotens konstruiert.

I.e. anstelle dieses

var link = svg.selectAll("path.link") 
    .data(links) 

Ich habe es dieses

var link = svg.selectAll("path.link") 
    .data(links, function(link) { var id = link.id + '->' + link.parent.id; return id; }); 

Probieren Sie es aus:

var data = [{ 
 
    "name": "Hazer 5000", 
 
    "parent": "CFO", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/stephen.jpg" 
 
    }, { 
 
    "name": "Employee 1", 
 
    "parent": "Hazer 5000", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/cory.jpg" 
 
    }, { 
 
    "name": "Analytics Area", 
 
    "parent": "Hazer 5000", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/matt.jpg" 
 
    }, { 
 
    "name": "Employee 2", 
 
    "parent": "Hazer 5000", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/XinheZhang.jpg" 
 
    }, { 
 
    "name": "Employee 3", 
 
    "parent": "Hazer 5000", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/craig.jpg" 
 
    }, { 
 
    "name": "Employee 4", 
 
    "parent": "Hazer 5000", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/youri.jpg" 
 
    }, { 
 
    "name": "Intern 1", 
 
    "parent": "Analytics Area", 
 
    "img": "" 
 
    }, { 
 
    "name": "Inter 2", 
 
    "parent": "Analytics Area", 
 
    "img": "" 
 
    }, { 
 
    "name": "CFO", 
 
    "parent": null, 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Brett.jpg" 
 
    }, { 
 
    "name": "CPA", 
 
    "parent": "CFO", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Wes.jpg" 
 
    }, { 
 
    "name": "Matt's wife", 
 
    "parent": "CPA", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Amy_R.jpg" 
 
    }, { 
 
    "name": "Employee 5", 
 
    "parent": "CPA", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/DavidBriley.jpg" 
 
    }, { 
 
    "name": "Employee 6", 
 
    "parent": "CPA", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/BrittanyAllred_.jpg" 
 
    }, { 
 
    "name": "Employee 7", 
 
    "parent": "CPA", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Shea.jpg" 
 
    }, { 
 
    "name": "Employee 8", 
 
    "parent": "Matt's wife", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Mindy.jpg" 
 
    }, { 
 
    "name": "Employee 9", 
 
    "parent": "Matt's wife", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Jessica_Stacy.jpg" 
 
    }, { 
 
    "name": "Employee 10", 
 
    "parent": "Matt's wife", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/FraleaneHudson.jpg" 
 
    },{ 
 
    "name": "Employee 11", 
 
    "parent": "Employee 9", 
 
    "img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/MeganPierce_.jpg" 
 
    },{ 
 
    "name": "Intern 3", 
 
    "parent": "Employee 8", 
 
    "img": "" 
 
    }, { 
 
    "name": "Intern 4", 
 
    "parent": "Employee 8", 
 
    "img": "" 
 
    } 
 

 
]; 
 

 
var margin = {top: 20, right: 120, bottom: 20, left: 120}, 
 
    width = 960 - margin.right - margin.left, 
 
    height = 800 - margin.top - margin.bottom; 
 

 
var i = 0, 
 
    duration = 750, 
 
    root; 
 

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

 
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 stratify = d3.stratify() 
 
    .id(function(d) { 
 
    return d.name;//This position 
 
    }) 
 
    .parentId(function(d) { 
 
    return d.parent; //What position this position reports to 
 
    }); 
 

 
var root = stratify(data); 
 

 
root.each(function(d) { 
 
    
 
    d.name = d.id; //transferring name to a name variable 
 
    d.id = i; //Assigning numerical Ids 
 
    i++; 
 
    
 
    }); 
 

 
    root.x0 = height/2; 
 
    root.y0 = 0; 
 

 
    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(root).descendants(), 
 
     links = nodes.slice(1); 
 

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

 
    // 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.y0 + "," + source.x0 + ")"; }) 
 
     .on("click", click); 
 

 
    nodeEnter.append("circle") 
 
     .attr("r", 1e-6) 
 
     .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; }); 
 

 
    nodeEnter.append("text") 
 
     .attr("x", function(d) { return d.children || d._children ? -10 : 10; }) 
 
     .attr("dy", ".35em") 
 
     .attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; }) 
 
     .text(function(d) { return d.name; }) 
 
     .style("fill-opacity", 1e-6); 
 

 
    // Transition nodes to their new position. 
 
    var nodeUpdate = node.merge(nodeEnter).transition() 
 
     .duration(duration) 
 
     .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; }); 
 

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

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

 
    // Transition exiting nodes to the parent's new position. 
 
    var nodeExit = node.exit().transition() 
 
     .duration(duration) 
 
     .attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; }) 
 
     .remove(); 
 

 
    nodeExit.select("circle") 
 
     .attr("r", 1e-6); 
 

 
    nodeExit.select("text") 
 
     .style("fill-opacity", 1e-6); 
 

 

 
    // Update the links… 
 
    var link = svg.selectAll("path.link") 
 
     .data(links, function(link) { var id = link.id + '->' + link.parent.id; return id; }); 
 
    
 
    // Transition links to their new position. 
 
    link.transition() 
 
     .duration(duration) 
 
     .attr("d", connector); 
 

 
    // 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, parent:{x: source.x0, y: source.y0}}; 
 
     return connector(o); 
 
     }); 
 

 
    // Transition links to their new position. 
 
    link.merge(linkEnter).transition() 
 
     .duration(duration) 
 
     .attr("d", connector); 
 

 
    // 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, parent:{x: source.x, y: source.y}}; 
 
     return connector(o); 
 
     }) 
 
     .remove(); 
 

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

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

 
function connector(d) { 
 
    return "M" + d.y + "," + d.x + 
 
    "C" + (d.y + d.parent.y)/2 + "," + d.x + 
 
    " " + (d.y + d.parent.y)/2 + "," + d.parent.x + 
 
    " " + d.parent.y + "," + d.parent.x; 
 
}
.node { 
 
    cursor: pointer; 
 
} 
 

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

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

 
.link { 
 
    fill: none; 
 
    stroke: #ccc; 
 
    stroke-width: 1.5px;
<script src="https://d3js.org/d3.v4.min.js"></script>

+0

'var link = svg.selectAll ("path.link") .data (Links, Funktion (d) {return d.id;}); 'war sogar genug, um es zum Laufen zu bringen. Danke, dass du mich in die richtige Richtung weist !! – Tekill

+1

@Yourinium oh yeah - 'd.id' funktioniert, weil es immer nur einen Link/Elternteil pro Kind gibt. – meetamit

+0

danke für deine hilfe @meetamit – Tekill