D3js的碰撞在一段时间后停止工作

问题描述 投票:1回答:2

我试图动态更新d3js力图上的节点,但碰撞在某些时候停止工作。这个问题似乎是由一个节点引起的,这个节点被准确地放在了svg画布的中心,但我不明白为什么。碰撞应该可以解决这个问题吧?有什么想法吗?

我附上一个最小的重现

var width = 400,
  height = 200;

var svg = d3.select("svg");

svg.attr("viewBox", [-width / 2, -height / 2, width, height]);

var nodes = [{
  radius: 20
}];

var simulation = d3
  .forceSimulation(nodes)
  .force("charge", d3.forceManyBody().strength(-50))
  .force(
    "collision",
    d3.forceCollide().radius(function(d) {
      return d.radius;
    })
  )
  .on("tick", ticked);

function ticked() {
  var u = d3.select("svg").selectAll("circle").data(nodes);

  u.enter()
    .append("circle")
    .attr("r", function(d) {
      return d.radius;
    })
    .merge(u)
    .attr("cx", function(d) {
      return d.x;
    })
    .attr("cy", function(d) {
      return d.y;
    });

  u.exit().remove();
}

function update() {
  nodes.push({
    radius: Math.random() * (20 - 5) + 5
  });
  node = svg.selectAll("circle").data(nodes);

  node
    .enter()
    .append("circle")
    .attr("r", (d) => d.radius);

  simulation.nodes(nodes);
}

setInterval(update, 100);
svg {
  background: blue
}

circle {
  fill: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="400" height="200" />
javascript d3.js
2个回答
2
投票

你的模拟冷却下来,然后停止,这时tick函数不再被调用。这是默认的行为。然而,你可以通过修改 alpha 衰减(simulation.alphaDecay()).

alpha衰减率决定了当前alpha向所需目标alpha内插的速度;由于默认的目标alpha为零,所以默认情况下,它控制了仿真冷却的速度。较高的衰减率会使模拟更快地稳定下来,但有可能卡在局部最小值中;较低的值会使模拟运行时间更长,但通常会在更好的布局上收敛。如果要让模拟永远以当前的 alpha 值运行,请将衰减率设置为零... (docs)

α、α衰减和α最小值的默认设置允许模拟运行大约300次迭代,在这段时间内,模拟会冷却并最终停止。

如果我们将阿尔法衰减设置为零,模拟将永远持续下去。然而,你可能要拨下来的力量,因为模拟仍然是热的,或删除节点,不再可见(因为模拟仍然跟踪他们)。我在下面的演示中既没有这样做。

var width = 400,
  height = 200;

var svg = d3.select("svg");

svg.attr("viewBox", [-width / 2, -height / 2, width, height]);

var nodes = [{
  radius: 20
}];

var simulation = d3
  .forceSimulation(nodes)
  .force("charge", d3.forceManyBody().strength(-50))
  .alphaDecay(0)
  .force(
    "collision",
    d3.forceCollide().radius(function(d) {
      return d.radius;
    })
  )
  .on("tick", ticked);

function ticked() {
  var u = d3.select("svg").selectAll("circle").data(nodes);

  u.enter()
    .append("circle")
    .attr("r", function(d) {
      return d.radius;
    })
    .merge(u)
    .attr("cx", function(d) {
      return d.x;
    })
    .attr("cy", function(d) {
      return d.y;
    });

  u.exit().remove();
}

function update() {
  nodes.push({
    radius: Math.random() * (20 - 5) + 5
  });
  node = svg.selectAll("circle").data(nodes);

  node
    .enter()
    .append("circle")
    .attr("r", (d) => d.radius);

  simulation.nodes(nodes);
}

setInterval(update, 100);
svg {
  background: blue
}

circle {
  fill: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="400" height="200" />

你也可以直接用Alpha值来操作。simulation.alpha()这是另一种方法来重新加热模拟,如果你想这样做,当一个新的节点被输入,例如。


0
投票

我忘了在更新节点数组后重新启动模拟。

simulation.nodes(nodes).restart();
© www.soinside.com 2019 - 2024. All rights reserved.