对分离元素使用力布局物理

Using the force-layout physics for seperated elements

我在 D3.js v4 中使用强制布局。现在我想单击一个节点并仅对那个节点使用像 collide 这样的力物理。

整个SVG上每个节点的模拟是这样的:

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))

现在我想更改 collide 我单击的一个节点的行为以推开或吸引其他节点。

有知道解决办法的人吗?我尝试了一些过滤功能和 stop/restart 布局,但它没有用。

你说:"I want to change the collide behavior for the one node i clicked on to push other nodes away or attract them".

如果您想使用 d3.forceCollide吸引 其他节点,那么您使用的工具是错误的。

根据 API:

The collision force treats nodes as circles with a given radius, rather than points, and prevents nodes from overlapping.

因此,collide基本上将其他节点推开,以避免重叠。

也就是说,此解决方案仅处理问题的第一部分:"I want to change the collide behavior for the one node i clicked on to push other nodes away".

有多种方法可以做到这一点。在我的解决方案中,当您单击一个节点时,它会更改绑定数据中的 r 属性:

d3.select(this).datum().r = 20;

无需重新绘制实际的 SVG 圆圈。这会将其他节点推开,使单击的节点保持相同大小。

这里是演示(点击节点):

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>