我有一个 D3 v4 力模拟,节点在屏幕上移动。每个节点都是由一个圆圈和其下方的一些文本组成的组。我如何排序,以便圆圈位于底层,文本始终位于顶层。一个圆圈与另一个圆圈重叠是可以的,但一个圆圈与文本顶部重叠是绝对不行的。这是我所得到的。目前,位于另一个节点前面的节点的圆圈将与该节点的文本重叠。
this.centerNode = this.d3Graph.selectAll(null)
.data(this.nodes.slice(10,20))
.enter()
.append("g")
this.centerNode.append("circle")
.attr("class", "backCircle")
.attr("r", 60)
.attr("fill", "red")
this.centerNode
.append("text")
.attr("fill", "black")
.attr("font-size", "20px")
.attr("y", -60)
.text("test text" )
最佳答案
使用当前的方法无法实现预期的结果。原因很简单:每组都有一个文字和一个圆圈。但是,绘画顺序取决于组的顺序:
<g>
<circle></circle>
<text></text><!-- this text here... -->
</g>
<g>
<circle></circle><!-- ...will be behind this circle here -->
<text></text>
</g>
<!-- etc... -->
因此,将 <g>
内的文本和圆圈分组元素,您将以给定的顺序绘制组,从而在文本上绘制一个圆圈(给定组的圆圈绘制在其之前的所有组的文本上)。
这是一个演示(Baz
圆圈将位于所有文本的顶部,Bar
圆圈将位于 Foo
文本的顶部):
var width = 300;
var height = 200;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var nodes = [{
name: "Foo"
}, {
name: "Bar"
}, {
name: "Baz"
}];
var links = [{
"source": 0,
"target": 1
}, {
"source": 0,
"target": 2
}];
var simulation = d3.forceSimulation()
.force("link", d3.forceLink())
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
var link = svg.selectAll(null)
.data(links)
.enter()
.append("line")
.style("stroke", "#ccc")
.style("stroke-width", 1);
var color = d3.scaleOrdinal(d3.schemeCategory20);
var node = svg.selectAll(null)
.data(nodes)
.enter()
.append("g")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
var nodeCircle = node.append("circle")
.attr("r", 20)
.attr("stroke", "gray")
.attr("stroke-width", "2px")
.attr("fill", function(d, i) {
return color(i)
});
var nodeTexts = node.append("text")
.style("fill", "black")
.attr("dx", 20)
.attr("dy", 8)
.text(function(d) {
return d.name;
});
simulation.nodes(nodes);
simulation.force("link")
.links(links);
simulation.on("tick", function() {
link.attr("x1", function(d) {
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
})
node.attr("transform", (d) => "translate(" + d.x + "," + d.y + ")")
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
解决方案
一种可能的解决方案是创建两个选择,一个用于圆圈,一个用于文本。将圆圈添加到之前,将文本添加到之后。记住使用相同的nodes
两者的数组:
var node = svg.selectAll(null)
.data(nodes)
.enter()
.append("circle")
//etc...
var nodeTexts = svg.selectAll(null)
.data(nodes)
.enter()
.append("text")
//etc...
这样,文本将始终位于圆圈顶部。
查看演示:
var width = 300;
var height = 200;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var nodes = [{
name: "Foo"
}, {
name: "Bar"
}, {
name: "Baz"
}];
var links = [{
"source": 0,
"target": 1
}, {
"source": 0,
"target": 2
}];
var simulation = d3.forceSimulation()
.force("link", d3.forceLink())
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
var link = svg.selectAll(null)
.data(links)
.enter()
.append("line")
.style("stroke", "#ccc")
.style("stroke-width", 1);
var color = d3.scaleOrdinal(d3.schemeCategory20);
var node = svg.selectAll(null)
.data(nodes)
.enter()
.append("circle")
.attr("r", 20)
.attr("stroke", "gray")
.attr("stroke-width", "2px")
.attr("fill", function(d, i) {
return color(i)
})
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
var nodeTexts = svg.selectAll(null)
.data(nodes)
.enter()
.append("text")
.style("fill", "black")
.attr("dx", 20)
.attr("dy", 8)
.text(function(d) {
return d.name;
});
simulation.nodes(nodes);
simulation.force("link")
.links(links);
simulation.on("tick", function() {
link.attr("x1", function(d) {
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
})
node.attr("transform", (d) => "translate(" + d.x + "," + d.y + ")")
nodeTexts.attr("transform", (d) => "translate(" + d.x + "," + d.y + ")")
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
关于d3.js - 如何根据元素而不是节点顺序对 D3 力模拟节点进行分层?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47492323/