d3-force - 节点被拖离链接,节拍不运行

时间:2017-10-30 16:32:17

标签: d3.js drag-and-drop drag d3-force-directed

我需要做一个"粘力布局"具有可拖动功能,并尝试在this link复制Mike Bostock的示例。因为作者在D3 v3中编写了该程序,所以我必须"升级"他的代码为D3 v4 。这意味着必须相应地更改d3-force和d3-drag语句。虽然原始示例中的逻辑流程很容易理解,但我仍然无法创建自己的版本(请参阅下面的代码)。问题:经过一段时间(2 - 3秒)后,节点被拖走但链接未更新

我最初的想法主要集中在拖动功能(.call(拖动)),但后来我发现" tick"在上面提到的时间之后,函数不再运行(我通过在tick函数中放入一个count变量得到它,然后是console.log它)。到目前为止,我的思绪是空洞的,无法进一步探索。

enter image description here

问题出在哪里?

var width = 960,
    height = 500;

var simulation = d3.forceSimulation()
    .force("charge", d3.forceManyBody().strength(-100))


d3.forceX(width)
d3.forceY(height)

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

var drag = d3.drag()
    .on("start", dragstarted)
    .on("drag", dragged)
    .on("end", dragended);
var link = svg.selectAll(".link")
var node = svg.selectAll(".node")

d3.json("graph.json", function (error, graph) {
    if (error) throw error;

    simulation.nodes(graph.nodes)
            .force("link", d3.forceLink(graph.links))
            .on("tick", tick);

    link = link.data(graph.links)
        .enter().append("line")
        .attr("class", "link");

    node = node.data(graph.nodes)
        .enter().append("circle")
        .attr("class", "node")
        .attr("r", 12)
        .on("dblclick", dblclick)
        .call(drag)

});

var count = 0;

function tick() {
    count++;
    console.log(count);
    link.attr("x1", function (d) { return d.source.x; })
        .attr("y1", function (d) { return d.source.y; })
        .attr("x2", function (d) { return d.target.x; })
        .attr("y2", function (d) { return d.target.y; });

    node.attr("cx", function (d) { return d.x; })
        .attr("cy", function (d) { return d.y; })
}

function dblclick(d) {
    d3.select(this).classed("fixed", d.fixed = false);
    // console.log("Is clicking");
}


function dragstarted(d) {
    d3.select(this).classed("fixed", d.fixed = true);
    console.log("Is dragging");
}

function dragged(d) {
    d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y);
}

function dragended(d) {
    d3.select(this).classed("active", false);
}

1 个答案:

答案 0 :(得分:1)

在这种力导向图中有两件事导致了问题:

  1. d3v4不使用d.fixed来修复节点
  2. d3v4与d3v3没有相同的默认alpha衰减
  3. 1:修复节点

    虽然您没有明确注意到这一点,但在图表更新链接数据的2-3秒内,节点在拖动后仍然会移动。

    这是因为,与d3v3相反,您需要使用d.fxd.fy手动修复坐标的x和y值; d.fixed不再修复您的节点。对于您的代码,这看起来像:

    function dragended(d) {
      d.fx = d3.event.x;
      d.fy = d3.event.y;
    }
    

    function dblclick(d) {
          d.fx = null;
          d.fy = null;
    }
    

    2:设置alpha衰减

    这会导致图表冻结。由于alpha衰减导致图形稳定后,不再调用tick函数。此时,您的拖动发生时没有对链接进行任何更新,导致节点和链接之间的连接断开。

    在d3v3中,默认情况下未定义alpha衰减,并且d3 可能会回退到不计算alpha衰减或使用零作为alpha衰减因子(导致相同的结果)。在d3v4中,alpha衰减设置为非零值:

      

    如果指定了衰减,则将alpha衰减率设置为指定值   数字在[0,1]范围内并返回此模拟。如果腐烂不是   指定,返回当前的alpha衰减率,默认为   0.0228 ... = 1 - pow(0.001,1 / 300)其中0.001是默认的最小alpha。

         

    alpha衰减率决定当前alpha的速度   插入所需的目标alpha;自默认   目标alpha为零,默认情况下,它控制的速度有多快   模拟冷却。衰减率越高,模拟就越稳定   更快,但有可能陷入局部最低限度;较低的价值   导致模拟运行时间更长,但通常会收敛   更好的布局。 让模拟永远在当前运行   alpha,将衰减率设置为零; 或者,设置目标alpha   大于最小alpha。

    所以,你可以使用:

    var simulation = d3.forceSimulation()
        .force("charge", d3.forceManyBody().strength(-100))
        .alphaDecay(0);
    

    (或者,将alphaTarget设置为适当的值,如上面文档引用中所述)。

    总而言之,这看起来像this