2016-07-11 7 views
5

Если вы играете с разборным деревом ниже, вы увидите, что, когда вы дойдете до конца дерева, а затем разверните и скомбинируете узлы, линии делают несколько дурацких вещей и я не совсем уверен, что влияет на поведение, или если моя переписывание enter link description here полностью не работает. Я удалил плоскую структуру данных и использовал stratify, чтобы преобразовать ее в макет дерева. Единственная проблема до сих пор - это линейные переходы ... любые мысли?d3.js v4 wacky link transition in collapsible tree пример

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>

ответ

6

Похоже, проблема с клавишами, используемыми для связывания данных удерживающего звена. Я имею в виду тот факт, что если вы вызываете .data(links) без 2-го аргумента, d3 использует i в качестве ключа, когда он вычисляет набор ввода/обновления/выхода, и поэтому, когда вы расширяете/сворачиваете, одна и та же ссылка может иметь разные значения i.

Я считаю, что я работал, и все, что я сделал, это добавить, что второй параметр как функция, которая строит уникальный идентификатор ссылки, объединяя идентификатор узла с идентификатором его родительского узла.

I.e. вместо этого

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

Я сделал это

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

Попробуйте:

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

'вар ссылка = svg.selectAll ("path.link") .data (ссылки, функция (d) {return d.id;}); 'было даже достаточно, чтобы заставить его работать. Спасибо, что указали мне в правильном направлении! – Tekill

+1

@Yourinium oh yeah - 'd.id' работает, потому что всегда есть только одна ссылка/родитель на ребенка. – meetamit

+0

спасибо за помощь @meetamit – Tekill

 Смежные вопросы

  • Нет связанных вопросов^_^