如何停止d3力图布局仿真?

时间:2023-02-03 21:25:52

Once you d3.layout.force()....start() a force layout simulation in D3, it keeps on going with also an active event.

一旦你d3.layout.force().... start()一个力布局仿真在D3中,它还继续与一个活跃的事件。

I want to set a timeout of 5 seconds to let the graph force layout simulation take shape and stop ( calling .stop() or alpha(0). This works to stop, but as soon as I drag a node the simulation starts again.

我想设置一个5秒的超时,让图形强制布局模拟成形和停止(调用.stop()或alpha(0)。这是可以停止的,但只要我拖动一个节点,模拟就又开始了。

The following code, also on easily testable from jsfiddle, shows at the last line that the stop() will be issued and the simulation immediately stops, but as soon as you drag any node, the simulation starts again. I think this is hardcoded in the force-drag. Is there a way to disable/unregister this ?

下面的代码,也可以从jsfiddle轻松地测试,在最后一行显示stop()将会被发布,并且模拟会立即停止,但是当您拖动任何节点时,模拟将再次启动。我认为这是硬编码在强制拖动。是否有办法禁用/取消注册?

var svgContainer = d3.select("#svgContainer");

var element0a = svgContainer.append("g").attr("class","node").attr("transform","translate(100,100)");
var element0b = element0a.append("rect").attr("x",0).attr("y",0).attr("width",20).attr("height",10).attr("fill","red");

var element1a = svgContainer.append("g").attr("class","node").attr("transform","translate(100,200)");
var element1b = element1a.append("rect").attr("x",0).attr("y",0).attr("width",20).attr("height",10).attr("fill","green");

var element2a = svgContainer.append("g").attr("class","node").attr("transform","translate(100,300)");
var element2b = element2a.append("rect").attr("x",0).attr("y",0).attr("width",20).attr("height",10).attr("fill","blue");

var nodeArray = new Array();
nodeArray[0] = { id : "000", label : "label 000", ui : element0a };
nodeArray[1] = { id : "001", label : "label 001", ui : element1a };
nodeArray[2] = { id : "002", label : "label 002", ui : element2a };

var linkArray = new Array();

var force = self.force = d3.layout.force()
    .nodes(nodeArray)
    .links(linkArray)
    .gravity(.05)
    .distance(80)
    .charge(-100)
    .size([600, 600])
    .start();

var node = svgContainer.selectAll("g.node")
    .data(nodeArray)
    .call(force.drag);


force.on("tick", function() {
      node.attr("transform", function(d) {return "translate(" + d.x + "," + d.y + ")";});
    });

// HERE !!!! Without the stop() the simulation goes fine. With the stop() the simulation will immediately stop, but will continue as soon as you drap something. I want the simulation to never start again. 

force.stop();

This is a screenshot of the example:

这是一个例子的截图:

如何停止d3力图布局仿真?

2 个解决方案

#1


9  

The simulation is restarted in the function that is called when a node is dragged internally. You can however overwrite this behaviour. The event name is "drag.force" and you can access it through force.drag. Instead of the usual behaviour, you can modify the coordinates directly without restarting the simulation like this:

当一个节点在内部被拖动时,该函数将重新启动这个模拟。但是,您可以改写这种行为。事件名称为“拖动”。你可以通过武力获取它。与通常的行为不同,您可以直接修改坐标,而不需要重新启动这样的模拟:

var node...
  .call(force.drag().on("drag.force", function() {
    d3.select(this).attr("transform", "translate(" + d3.event.x + "," + d3.event.y + ")");
  }));

This corresponds to the normal drag behavior. Before you do this, it is advisable to stop the simulation to prevent the drag event and force ticks from interfering. Simply call force.stop() at the beginning of your modified event handler.

这相当于正常的拖动行为。在进行此操作之前,最好停止模拟,以防止拖拽事件和强制时钟干扰。在修改的事件处理程序开始时,只需调用force.stop()。

Finally you'll need to reset the origin for the drag events, otherwise you'll notice that dragged elements "jump" back to their original position when you drag them again. This is done in similar fashion to modifying the event handler. You simply get the current translation and return the coordinates in the expected format.

最后,您需要重置拖动事件的原点,否则您会注意到,当您再次拖动元素时,拖动元素“跳转”回原来的位置。这是用类似的方式来修改事件处理程序的。您只需获得当前的转换并以预期的格式返回坐标。

var node...
  .call(force.drag().origin(function() {
    var t = d3.transform(d3.select(this).attr("transform")).translate;
    return {x: t[0], y: t[1]};
  }));

The complete code is in the updated jsfiddle here.

完整的代码在更新的jsfiddle中。

#2


1  

Setting up an appropiate "friction" should do it, though you don't control the time it will need to stop:

建立一种认可的“摩擦”应该做到这一点,尽管你不能控制它需要停止的时间:

var force = self.force = d3.layout.force()
  .nodes(nodeArray)
  .links(linkArray)
  .gravity(.05)
  .distance(80)
  .charge(-100)
  .friction(0.5)
  .size([600, 600])
  .start();

#1


9  

The simulation is restarted in the function that is called when a node is dragged internally. You can however overwrite this behaviour. The event name is "drag.force" and you can access it through force.drag. Instead of the usual behaviour, you can modify the coordinates directly without restarting the simulation like this:

当一个节点在内部被拖动时,该函数将重新启动这个模拟。但是,您可以改写这种行为。事件名称为“拖动”。你可以通过武力获取它。与通常的行为不同,您可以直接修改坐标,而不需要重新启动这样的模拟:

var node...
  .call(force.drag().on("drag.force", function() {
    d3.select(this).attr("transform", "translate(" + d3.event.x + "," + d3.event.y + ")");
  }));

This corresponds to the normal drag behavior. Before you do this, it is advisable to stop the simulation to prevent the drag event and force ticks from interfering. Simply call force.stop() at the beginning of your modified event handler.

这相当于正常的拖动行为。在进行此操作之前,最好停止模拟,以防止拖拽事件和强制时钟干扰。在修改的事件处理程序开始时,只需调用force.stop()。

Finally you'll need to reset the origin for the drag events, otherwise you'll notice that dragged elements "jump" back to their original position when you drag them again. This is done in similar fashion to modifying the event handler. You simply get the current translation and return the coordinates in the expected format.

最后,您需要重置拖动事件的原点,否则您会注意到,当您再次拖动元素时,拖动元素“跳转”回原来的位置。这是用类似的方式来修改事件处理程序的。您只需获得当前的转换并以预期的格式返回坐标。

var node...
  .call(force.drag().origin(function() {
    var t = d3.transform(d3.select(this).attr("transform")).translate;
    return {x: t[0], y: t[1]};
  }));

The complete code is in the updated jsfiddle here.

完整的代码在更新的jsfiddle中。

#2


1  

Setting up an appropiate "friction" should do it, though you don't control the time it will need to stop:

建立一种认可的“摩擦”应该做到这一点,尽管你不能控制它需要停止的时间:

var force = self.force = d3.layout.force()
  .nodes(nodeArray)
  .links(linkArray)
  .gravity(.05)
  .distance(80)
  .charge(-100)
  .friction(0.5)
  .size([600, 600])
  .start();