当我在d3中使用树形图时,我通过在树形图中添加节点来获得我想要的行为。但是,当我向图表添加节点时,节点(和路径)会被放置,但节点不会平衡,之前的节点也不会移动。
我在这里发布了整个代码的工作版本:https://codepen.io/auser/pen/mwwVJL
JS代码(为了完整起见):
const pathGraph = (eleName, treeData, opts = {}) => {
var margin = { top: 40, right: 90, bottom: 50, left: 90 },
width = 660 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
const treemap = d3.tree().size([width, height]);
const svg = d3
.select(eleName)
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
let g = svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
const updateData = newData => {
// assigns the data to a hierarchy using parent-child relationships
let nodes = treemap(d3.hierarchy(newData));
const color = d3.scaleOrdinal(d3.schemeCategory10).domain(d3.range(0, 8));
// adds the links between the nodes
const link = g
.selectAll(".link")
.data(nodes.descendants().slice(1))
.enter()
.append("path")
.attr("class", "link")
.style("stroke-width", 1)
.attr("d", function(d) {
return (
"M" + d.x + "," + d.y + "C" + d.x + "," + (d.y + d.parent.y) / 2 + " " + d.parent.x + "," + (d.y + d.parent.y) / 2 + " " + d.parent.x + "," + d.parent.y
);
});
link.exit().remove();
// adds each node as a group
const node = g
.selectAll(".node")
.data(nodes.descendants())
.enter()
.append("g")
.attr("class", function(d) {
return "node" + (d.children ? " node--internal" : " node--leaf");
})
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
node.exit().remove();
const parentTree = (d) => {
let nodeLinks = []
while(d.parent) {
nodeLinks.push(d)
d = d.parent
}
nodeLinks = nodeLinks.concat(d)
return nodeLinks
}
const activeLink = (d, o) => {
if (d === o || d.parent === o) return true;
}
node
.on('mouseover', function(d) {
const data = d3.select(this)
const linkedNodes = parentTree(d)
link
.style('stroke-width', o => activeLink(d, o) ? 4 : 1)
.style('stroke', o => activeLink(d, o) ? 'red' : '#333')
.transition(500)
rect
.style('stroke-width', o => activeLink(d, o) ? 4 : 1)
.transition(500)
})
.on('mouseout', d => {
const data = d3.select(this);
// console.log('d ->', d)
link
.style('stroke-width', 1)
.style('stroke', '#333')
rect
.style('stroke-width', 1)
})
// adds the circle to the node
const rect = node
.append("rect")
.attr("height", 50)
.attr("width", 50)
.style("fill", (d, i) => color(i))
.attr("x", "-0.7em");
// adds the text to the node
node
.append("text")
.attr("dy", ".52em")
.attr("y", function(d) {
return d.children ? -18 : 20;
})
.attr("dx", "-.2em")
.style("text-anchor", "middle")
.text(function(d) {
return d.data.name;
});
};
updateData(treeData);
return updateData;
};
const data = {
"name": "Root",
"children": [
{
"name": "A",
"children": [
{ "name": "B" },
{ "name": "C" }
]
},
{ "name": "D" },
{ "name": "E",
"children":[
{ "name": "F"}
] }
]
};
const mount = document.querySelector('#treea')
const updateData = pathGraph(mount, data)
setTimeout(function() {
data.children[2].children.push({ name: "H" })
updateData(data, { update: true })
}, 2000)
.tree .node rect, .tree .node circle {
fill: blue;
rounding: 5px;
}
.tree .link {
fill: none;
stroke: #222;
stroke-opacity: 1;
stroke-width: 1.5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js"></script>
<svg id="treea" class="tree"></svg>
非常感谢任何帮助......
答案 0 :(得分:1)
你需要一个合适的&#34;输入&#34;,&#34;更新&#34;和&#34;退出&#34;选择。像这样:
//this is the update selection
const link = g
.selectAll(".link")
.data(nodes.descendants().slice(1));
//this is the enter selection, up to the 'merge'
link.enter()
.append("path")
.attr("class", "link")
.merge(link)//from now one, update + enter
.style("stroke-width", 1)
.attr("d", function(d) {
return (
"M" + d.x + "," + d.y + "C" + d.x + "," +
(d.y + d.parent.y) / 2 + " " + d.parent.x + "," +
(d.y + d.parent.y) / 2 + " " + d.parent.x + "," + d.parent.y
);
});
//this is the exit selection
link.exit().remove();
以下是更新的Codepen:https://codepen.io/anon/pen/RggVPO?editors=0010
这里是Stack片段:
const pathGraph = (eleName, treeData, opts = {}) => {
var margin = { top: 40, right: 90, bottom: 50, left: 90 },
width = 660 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
const treemap = d3.tree().size([width, height]);
const svg = d3
.select(eleName)
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
let g = svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
const updateData = newData => {
// assigns the data to a hierarchy using parent-child relationships
let nodes = treemap(d3.hierarchy(newData));
const color = d3.scaleOrdinal(d3.schemeCategory20).domain(d3.range(0, 8));
// adds the links between the nodes
const link = g
.selectAll(".link")
.data(nodes.descendants().slice(1));
link.enter()
.append("path")
.attr("class", "link")
.merge(link)
.style("stroke-width", 1)
.attr("d", function(d) {
return (
"M" + d.x + "," + d.y + "C" + d.x + "," + (d.y + d.parent.y) / 2 + " " + d.parent.x + "," + (d.y + d.parent.y) / 2 + " " + d.parent.x + "," + d.parent.y
);
}).lower();
link.exit().remove();
const node = g
.selectAll(".node")
.data(nodes.descendants())
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
const nodeEnter = node.enter()
.append("g")
.attr("class", function(d) {
return "node " + (d.children ? " node--internal" : " node--leaf");
})
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
node.exit().remove();
const parentTree = (d) => {
let nodeLinks = []
while(d.parent) {
nodeLinks.push(d)
d = d.parent
}
nodeLinks = nodeLinks.concat(d)
return nodeLinks
}
const activeLink = (d, o) => {
if (d === o || d.parent === o) return true;
}
node
.on('mouseover', function(d) {
const data = d3.select(this)
const linkedNodes = parentTree(d)
link
.style('stroke-width', o => activeLink(d, o) ? 4 : 1)
.style('stroke', o => activeLink(d, o) ? 'red' : '#333')
.transition(500)
rect
.style('stroke-width', o => activeLink(d, o) ? 4 : 1)
.transition(500)
})
.on('mouseout', d => {
const data = d3.select(this);
// console.log('d ->', d)
link
.style('stroke-width', 1)
.style('stroke', '#333')
rect
.style('stroke-width', 1)
})
// adds the circle to the node
const rect = nodeEnter
.append("rect")
.attr("height", 50)
.attr("width", 50)
.style("fill", (d, i) => color(i))
.style('padding', 5)
.attr("rx", 6)
.attr("ry", 6)
.attr("x", "-0.7em");
// adds the text to the node
nodeEnter
.append("text")
.attr("dy", "0.8em")
.attr("y", function(d) {
return d.children ? -18 : 20;
})
.attr("dx", "0.8em")
.style("text-anchor", "middle")
.text(function(d) {
return d.data.name;
});
node.select("rect").attr("x", "-0.7em");
};
updateData(treeData);
return updateData;
};
const data = {
"name": "Root",
"children": [
{
"name": "A",
"children": [
{ "name": "B" },
{ "name": "C" }
]
},
{ "name": "D" },
{ "name": "E",
"children":[
{ "name": "F"}
] }
]
};
const mount = document.querySelector('#treea')
const updateData = pathGraph(mount, data)
setTimeout(function() {
data.children[2].children.push({ name: "H" })
updateData(data, { update: true })
}, 2000)
&#13;
.tree .node rect, .tree .node circle {
fill: blue;
rounding: 5px;
}
.tree .link {
fill: none;
stroke: #222;
stroke-opacity: 1;
stroke-width: 1.5px;
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js"></script>
<svg id="treea" class="tree"></svg>
&#13;
PS:节点的事情比较复杂,因为你有一组有矩形和文本的组。我很快就改变了选择,但我建议你相应地重构那部分代码。