Как остановить симуляцию графов сил 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();
Это скриншот из примера:
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 здесь.
Настройка подходящего "трения" должна сделать это, хотя вы не контролируете время, которое нужно будет остановить:
var force = self.force = d3.layout.force()
.nodes(nodeArray)
.links(linkArray)
.gravity(.05)
.distance(80)
.charge(-100)
.friction(0.5)
.size([600, 600])
.start();