对分离元素使用强制布局物理

时间:2017-01-28 22:24:05

标签: javascript d3.js nodes force-layout

我在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行为,以推开其他节点或吸引它们。

有人知道解决方案吗?我尝试了一下过滤功能并停止/重新启动布局,但它没有用。

1 个答案:

答案 0 :(得分:1)

你说:“我想改变我点击的一个节点的碰撞行为以推开其他节点或吸引他们”

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

根据API

  

碰撞力将节点视为具有给定半径而不是点的圆,并防止节点重叠。

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

话虽这么说,这个解决方案只处理问题的第一部分:“我想改变我点击的推送其他节点的一个节点的碰撞行为”

有不同的方法可以做到这一点。在我的解决方案中,当您单击某个节点时,它会更改绑定数据中的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>