问题
I use the force layout in D3.js v4. Now I want to click on a node and use the force physics like collide
for that one node only.
The simulation for every node on the entire SVG is this:
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) {
return d.index
}))
.force("collide", d3.forceCollide(function(d) {
return d.r + 8
}).iterations(16))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(chartWidth / 2, chartWidth / 2))
Now I want to change the collide
behavior for the one node I clicked on to push other nodes away or attract them.
Is there someone who knows a solution? I tried a bit with a filter function and stop/restart the layout, but it did not work.
回答1:
You said: "I want to change the collide behavior for the one node i clicked on to push other nodes away or attract them".
If you want to attract the other nodes using d3.forceCollide
, you're using the wrong tool for the task.
According to the API:
The collision force treats nodes as circles with a given radius, rather than points, and prevents nodes from overlapping.
Thus, collide
basically pushes the other nodes away, to avoid overlapping.
That being said, this solution deals only with the first part of your problem: "I want to change the collide behavior for the one node i clicked on to push other nodes away".
There are different ways for doing this. In my solution, when you click a node, it changes the r
property in the bound data:
d3.select(this).datum().r = 20;
Without redrawing the actual SVG circle. That pushes the other nodes away, maintaining the clicked node at the same size.
Here is the demo (click on the nodes):
var svg = d3.select("svg");
var colour = d3.scaleOrdinal(d3.schemeCategory10);
var data = d3.range(30).map(d => ({
r: 6
}));
var simulation = d3.forceSimulation(data)
.force("x", d3.forceX(150).strength(0.05))
.force("y", d3.forceY(75).strength(0.05))
.force("collide", d3.forceCollide(function(d) {
return d.r + 1;
}));
var node = svg.selectAll(".circles")
.data(data)
.enter()
.append("circle")
.attr("r", d => d.r)
.attr("fill", (d, i) => colour(i));
node.on("click", function(d) {
d3.selectAll("circle").data().forEach(d => d.r = 6);
d3.select(this).datum().r = 20;
simulation.nodes(data);
simulation.alpha(0.8).restart();
})
simulation.nodes(data)
.on("tick", d => {
node.attr("cx", d => d.x).attr("cy", d => d.y);
});
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg></svg>
来源:https://stackoverflow.com/questions/41915510/using-the-force-layout-physics-for-seperated-elements