Using the force-layout physics for seperated elements

别等时光非礼了梦想. 提交于 2019-12-12 10:14:33

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!