1

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

我最初的想法集中在拖动函数(.call(drag)),但后来我发现“tick”函数在上述时间后不再运行(我通过在tick中放置一个计数变量得到它函数,然后 console.log 它)。至此,我的脑海里一片空白,无法进一步探索。

在此处输入图像描述

问题出在哪里?

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);
}
4

1 回答 1

2

在这个力有向图中有两件事会导致问题:

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

1:固定节点

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

这是因为,与 d3v3 不同,您需要使用d.fxand手动修复坐标的 x 和 y 值d.fyd.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 衰减而稳定后,不再调用刻度函数。在这一点上,你的拖拽发生在没有对链接进行任何更新的情况下,导致节点和链接之间的连接断开。

在 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 设置为适当的值,如上面的文档引用中所述)。

总而言之,这看起来像这样

于 2017-10-30T17:47:25.637 回答