Как остановить симуляцию макета графа силы d3?

После того, как вы d3.layout.force()....start() симулируете макет принудительно в D3, он продолжает работать с активным событием.

Я хочу установить тайм-аут в 5 секунд, чтобы симуляция макета графа приняла форму и остановилась (вызов .stop() или alpha(0). Это работает для остановки, но как только я перетаскиваю узел, симуляция начинается снова.

Следующий код, также легко тестируемый из 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();

Это скриншот примера:

введите здесь описание изображения


person gextra    schedule 19.04.2013    source источник
comment
Похоже, вы перезапускаете симуляцию, когда происходит событие перетаскивания. Проверьте, куда вы звоните .start().   -  person Lars Kotthoff    schedule 19.04.2013
comment
На самом деле я вызываю start() только один раз, в самом начале. Я добавил jsfiddle с тестируемым сценарием. Проверьте последнюю строку: jsfiddle.net/guidoextras/trzfw.   -  person gextra    schedule 19.04.2013


Ответы (2)


Моделирование перезапускается в функции, которая вызывается при внутреннем перетаскивании узла. Однако вы можете перезаписать это поведение. Имя события — «drag.force», и вы можете получить к нему доступ через force.drag. Вместо обычного поведения вы можете изменить координаты напрямую, не перезапуская симуляцию следующим образом:

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

Это соответствует нормальному поведению перетаскивания. Прежде чем вы это сделаете, рекомендуется остановить симуляцию, чтобы предотвратить вмешательство события перетаскивания и принудительных галочек. Просто вызовите force.stop() в начале вашего измененного обработчика событий.

Наконец, вам нужно сбросить исходную точку для событий перетаскивания, иначе вы заметите, что перетаскиваемые элементы «прыгают» обратно в исходное положение, когда вы снова перетаскиваете их. Это делается аналогично изменению обработчика события. Вы просто получаете текущий перевод и возвращаете координаты в ожидаемом формате.

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

Полный код находится в обновленном jsfiddle здесь.

person Lars Kotthoff    schedule 19.04.2013

Настройка соответствующего «трения» должна сделать это, хотя вы не контролируете время, когда оно должно остановиться:

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