2017-03-03 57 views
0

我一直在尝试从D3.js v3迁移到第4版。我已阅读更改日志并更新了所有功能,但是我现在对角线函数被删除后无法呈现从源节点到目标节点的路径。无法在D3.js中渲染从源到目标的路径,将v3迁移到v4

我正在使用由Python脚本通过HTML和d3.js生成的分析树。 Python的脚本生成的HMTL文件,在这里它与D3.js版本上运行3

function drawTree(){ 
 
var margin = {top: 20, right: 120, bottom: 20, left: 120}, 
 
    width = 1060 - margin.right - margin.left, 
 
    height = 600 - margin.top - margin.bottom; 
 
    
 
var i = 0, 
 
    duration = 750,// animation duration 
 
    root;// stores the tree structure in json format 
 

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

 
var edge_weight = d3.scale.linear() 
 
\t \t \t \t \t .domain([0, 100]) 
 
        .range([0, 100]); 
 

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

 
// adding the svg to the html structure 
 
var svg = d3.select("div#viz").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 treeData = 
 
    { 
 
    "name": "Grandparent", 
 
    "size" : 100, 
 
    "children": [ 
 
     { 
 
     "name": "Parent A", 
 
     "size": 70, 
 
     "children": [ 
 
      { "name": "Son of A", 
 
      "size" : 30, 
 
      "children": [ 
 
       { "name": "grandson of A", 
 
       "size" : 3}, 
 
       { "name": "grandson 2 of A", 
 
       "size" : 2}, 
 
       { "name": "grandson 3 of A", 
 
       "size" : 5}, 
 
       { "name": "grandaughter of A", 
 
       "size" : 20, 
 
       "children": [ 
 
        { "name": "great-grandson of A", 
 
        "size" : 15}, 
 
        { "name": "great-grandaughter of A", 
 
        "size" : 5} 
 
       ] 
 
       } 
 
      ], 
 
      }, 
 
      { "name": "Daughter of A" , 
 
     \t \t "size" : 40 
 
      } 
 
     ] 
 
     }, 
 
     { "name": "Parent B", 
 
     "size" : 30 }], 
 
    \t }; 
 
    
 
    
 
    edge_weight.domain([0,treeData.size]); 
 
    
 
// Assigns parent, children, height, depth 
 
root = treeData; 
 
root.x0 = height/2; 
 
root.y0 = 0; 
 
    
 
    root.children.forEach(collapse); 
 
    update(root); 
 

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

 
/** 
 
* Updates the node. 
 
* cloppases and expands the node bases on the structure of the source 
 
* all 'children' nodes are expanded and '_children' nodes collapsed 
 
* @param {json structure} source 
 
*/ 
 
function update(source) { 
 

 
    // Compute the new tree layout. 
 
    var nodes = tree.nodes(root).reverse(), 
 
     links = tree.links(nodes); 
 

 
    // 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('class', 'node') 
 
     .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.transition() 
 
     .duration(duration) 
 
     .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; }); 
 

 
    nodeUpdate.select("circle") 
 
     .attr("r", function(d){ console.log(">>>>>>>>", d);return edge_weight(d.size/2);}) 
 
     .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… 
 
    // Update the links… 
 
    var link = svg.selectAll("path.link") 
 
     .data(links, function(d) { return d.target.id; }); 
 

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

 
    // Transition links to their new position. 
 
    link.transition() 
 
     .duration(duration) 
 
     .attr("d", function(d){ 
 
     /* calculating the top shift */ 
 
     var source = {x: d.source.x - edge_weight(calculateLinkSourcePosition(d)), y: d.source.y}; 
 
     var target = {x: d.target.x, y: d.target.y}; 
 
     return diagonal({source: source, target: target}); 
 
     }) 
 
     .attr("stroke-width", function(d){ 
 
     \t return edge_weight(d.target.size); 
 
     }); 
 

 
// 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}; 
 
     return diagonal({source: o, target: o}); 
 
     }) 
 
     .remove(); 
 

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

 
/** 
 
* Calculates the source y-axis position of the link. 
 
* @param {json structure} link 
 
*/ 
 
function calculateLinkSourcePosition(link) { 
 
\t targetID = link.target.id; 
 
\t var childrenNumber = link.source.children.length; 
 
\t var widthAbove = 0; 
 
\t for (var i = 0; i < childrenNumber; i++) 
 
\t { 
 
\t \t if (link.source.children[i].id == targetID) 
 
\t \t { 
 
\t \t \t // we are done 
 
\t \t \t widthAbove = widthAbove + link.source.children[i].size/2; 
 
\t \t \t break; 
 
\t \t }else { 
 
\t \t \t // keep adding 
 
\t \t \t widthAbove = widthAbove + link.source.children[i].size 
 
\t \t } 
 
\t } 
 
\t return link.source.size/2 - widthAbove; 
 
} 
 

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

 
/* 
 
* Collapses the node d and all the children nodes of d 
 
* @param {node} d 
 
*/ 
 
function collapse(d) { 
 
    if (d.children) { 
 
    d._children = d.children; 
 
    d._children.forEach(collapse); 
 
    d.children = null; 
 
    } 
 
} 
 

 
/* 
 
* Collapses the node in the tree 
 
*/ 
 
function collapseAll() { 
 
\t root.children.forEach(collapse); 
 
\t update(root); 
 
} 
 

 
/* 
 
* Expands the node d and all the children nodes of d 
 
* @param {node} d 
 
*/ 
 
function expand(d) { 
 
\t if (d._children) { 
 
\t \t d._children = null; 
 
\t } 
 
\t if (d.children) { 
 
\t \t d.children.forEach(expand); 
 
\t } 
 
\t 
 
} 
 
/* 
 
* Expands all the nodes in the tree 
 
*/ 
 
function expandAll() { 
 
\t root.children.forEach(expand); 
 
\t update(root); 
 
} 
 
}
.node { 
 
    cursor: pointer; 
 
} 
 

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

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

 
.link { 
 
    fill: none; 
 
    /*stroke: steelblue;*/ 
 
    opacity: 0.3; 
 
    /*stroke-width: 1.5px;*/ 
 
} 
 

 
#levels{ 
 
    margin-left: 120px; 
 
}
<!DOCTYPE html> 
 
<meta charset="utf-8"> 
 
<body onLoad="drawTree()"> 
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.0.0/d3.min.js"></script> 
 

 
<button type="button" onclick="collapseAll()">Collapse All</button> 
 
<button type="button" onclick="expandAll()">Expand All</button> 
 
<div id="viz"></div> 
 
</body>

这里就是我与V4迁移了。

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

 
var i = 0, 
 
    duration = 750, // animation duration 
 
    root; // stores the tree structure in json format 
 

 

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

 
var edge_weight = d3.scaleLinear() 
 
    .domain([0, 100]) 
 
    .range([0, 100]); 
 

 

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

 
    path = `M ${s.y} ${s.x} 
 
      C ${(s.y + d.y)/2} ${s.x}, 
 
       ${(s.y + d.y)/2} ${d.x}, 
 
       ${d.y} ${d.x}` 
 

 
    return path 
 
} 
 

 
// adding the svg to the html structure 
 
var svg = d3.select("div#viz").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 treeData = { 
 
    "name": "Grandparent", 
 
    "size": 100, 
 
    "children": [{ 
 
     "name": "Parent A", 
 
     "size": 70, 
 
     "children": [{ 
 
      "name": "Son of A", 
 
      "size": 30, 
 
      "children": [{ 
 
       "name": "grandson of A", 
 
       "size": 3 
 
      }, 
 
      { 
 
       "name": "grandson 2 of A", 
 
       "size": 2 
 
      }, 
 
      { 
 
       "name": "grandson 3 of A", 
 
       "size": 5 
 
      }, 
 
      { 
 
       "name": "grandaughter of A", 
 
       "size": 20, 
 
       "children": [{ 
 
        "name": "great-grandson of A", 
 
        "size": 15 
 
       }, 
 
       { 
 
        "name": "great-grandaughter of A", 
 
        "size": 5 
 
       } 
 
       ] 
 
      } 
 
      ], 
 
     }, 
 
     { 
 
      "name": "Daughter of A", 
 
      "size": 40 
 
     } 
 
     ] 
 
    }, 
 
    { 
 
     "name": "Parent B", 
 
     "size": 30 
 
    } 
 
    ], 
 
}; 
 

 

 
edge_weight.domain([0, treeData.size]); 
 

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

 
root.children.forEach(collapse); 
 
update(root); 
 

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

 
/** 
 
* Updates the node. 
 
* cloppases and expands the node bases on the structure of the source 
 
* all 'children' nodes are expanded and '_children' nodes collapsed 
 
* @param {json structure} source 
 
*/ 
 
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; 
 
    }); 
 

 
    // 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('class', 'node') 
 
    .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 = nodeEnter.merge(node); 
 

 
    nodeUpdate.transition() 
 
    .duration(duration) 
 
    .attr("transform", function(d) { 
 
     return "translate(" + d.y + "," + d.x + ")"; 
 
    }); 
 

 
    nodeUpdate.select("circle") 
 
    .attr("r", function(d) { 
 
     return edge_weight(d.data.size/2); 
 
    }) 
 
    .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(d) { 
 
     return d.id; 
 
    }); 
 
    //.data(links, function(d) { return d.target.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) { 
 
     console.log("linkEnter", d); 
 
     var o = { 
 
     x: source.x, 
 
     y: source.y 
 
     } 
 
     console.log("o", o); 
 
     return diagonal(o, o) 
 
    }) 
 
    .attr("stroke", function(d) { 
 
     return "cyan"; 
 
    }); 
 

 
    // Transition links to their new position. 
 
    link.transition() 
 
    .duration(duration) 
 
    .attr("d", function(d) { 
 
     console.log("lala", d); 
 
     /* calculating the top shift */ 
 
     var source = { 
 
     x: d.x - edge_weight(calculateLinkSourcePosition(d)), 
 
     y: d.y 
 
     }; 
 
     var target = { 
 
     x: d.parent.x, 
 
     y: d.parent.y 
 
     }; 
 
     return diagonal({ 
 
     source: source, 
 
     target: target 
 
     }); 
 
    }) 
 
    .attr("stroke-width", function(d) { 
 
     return edge_weight(d.target.size); 
 
    }); 
 

 
    // 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 
 
     }; 
 
     return diagonal({ 
 
     source: o, 
 
     target: o 
 
     }); 
 
    }) 
 
    .remove(); 
 

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

 
/** 
 
* Calculates the source y-axis position of the link. 
 
* @param {json structure} link 
 
*/ 
 
function calculateLinkSourcePosition(link) { 
 
    targetID = link.target.id; 
 
    var childrenNumber = link.source.children.length; 
 
    var widthAbove = 0; 
 
    for (var i = 0; i < childrenNumber; i++) { 
 
    if (link.source.children[i].id == targetID) { 
 
     // we are done 
 
     widthAbove = widthAbove + link.source.children[i].size/2; 
 
     break; 
 
    } else { 
 
     // keep adding 
 
     widthAbove = widthAbove + link.source.children[i].size 
 
    } 
 
    } 
 
    return link.source.size/2 - widthAbove; 
 
} 
 

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

 
/* 
 
* Collapses the node d and all the children nodes of d 
 
* @param {node} d 
 
*/ 
 
function collapse(d) { 
 
    if (d.children) { 
 
    d._children = d.children; 
 
    d._children.forEach(collapse); 
 
    d.children = null; 
 
    } 
 
} 
 

 
/* 
 
* Collapses the node in the tree 
 
*/ 
 
function collapseAll() { 
 
    root.children.forEach(collapse); 
 
    update(root); 
 
} 
 

 
/* 
 
* Expands the node d and all the children nodes of d 
 
* @param {node} d 
 
*/ 
 
function expand(d) { 
 
    if (d._children) { 
 
    d.children = d._children; 
 
    d._children = null; 
 
    } 
 
    if (d.children) { 
 
    d.children.forEach(expand); 
 
    } 
 

 
} 
 
/* 
 
* Expands all the nodes in the tree 
 
*/ 
 
function expandAll() { 
 
    root.children.forEach(expand); 
 
    update(root); 
 
}
.node { 
 
    cursor: pointer; 
 
} 
 

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

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

 
.link { 
 
    fill: none; 
 
    /*stroke: steelblue;*/ 
 
    opacity: 0.3; 
 
    /*stroke-width: 1.5px;*/ 
 
} 
 

 
#levels { 
 
    margin-left: 120px; 
 
}
<body> 
 
    <script src="http://d3js.org/d3.v4.min.js"></script> 
 
    <button type="button" onclick="collapseAll()">Collapse All</button> 
 
    <button type="button" onclick="expandAll()">Expand All</button> 
 
    <div id="viz"></div> 
 
</body>

最有可能的,我只是盲目的,你会立即看到我做错了什么,但我在这个一直盯着现在一段时间...

预先感谢任何帮助!

+0

检查我的答案复制(https://stackoverflow.com/q/40845121/5768908)。刚刚引入了类似的方法。 –

回答

1

我目前工作的一个类似的项目,我自己并推断这可能是非常有益的..看来你真的很接近,只是需要调整一些小的路径到达父/子节点..

基本上“源”现在是“父母”,没有“目标”。你可以看到大部分的更新,其中注释掉的行是v3,下面是v4更新。例如:

link.target.id --> link.id 
link.source.children.length --> link.parent.children.length 
link.source.size --> link.parent.data.size 

在整个代码中还有一些其他的更新更新。我无法完全工作的一件事是“全部展开/全部折叠”按钮。 “全部展开”似乎工作确定,但“全部折叠”似乎独自离开的链接路径..

这里的工作小提琴:https://jsfiddle.net/jufra0b2/

我怀疑有什么东西可以做,到出口环节,但不确定。无论如何,这是朝着正确方向迈出的一步。希望你找出其余的好..