D3力导向图添加新节点导致x&y为NaN

时间:2018-09-08 12:37:08

标签: javascript d3.js

当我单击一个节点时,我希望向其添加一个新节点。它们都应该都有标签(我正在尝试建立同义词库可视化文件。)

我是D3的新手,如果您需要更详细地说明事情,我深表歉意。

到目前为止,这是我的代码:

var width = 960;
var height = 500;

var force = d3.layout.force()
    .gravity(0.05)
    .distance(100)
    .charge(-100)
    .size([width, height])
    .nodes([{ "name": "One", "group": 1 }])
    .start();

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

var nodes = force.nodes();
var links = force.links();

var link = svg.selectAll(".link")
    .data(links)
    .enter().append("line")
    .attr("class", "link");

var node = svg.selectAll(".node")
    .data(force.nodes())
    .enter().append("g")
    .attr("class", "node")
    .call(force.drag);

node.append("circle")
    .attr("r", 10)
    .on("mousedown", onClick);

node.append("text")
    .attr("dx", 12)
    .attr("dy", ".35em")
    .text(d => d.name);

force.on("tick", function() {
    link.attr("x1", d => d.source.x)
        .attr("y1", d => d.source.y)
        .attr("x2", d => d.target.x)
        .attr("y2", d => d.target.y);

    node.attr("transform", d => {
        // d.x & d.y are NaN for new nodes
        return "translate(" + d.x + "," + d.y + ")";
    });
});

restart();

function restart() {
    node = node.data(nodes);

    node.enter().append("g")
        .attr("class", "node")
        .call(force.drag);
    node.append("circle")
        .attr("r", 10)
        .on("mousedown", onClick);
    node.append("text")
        .attr("dx", 12)
        .attr("dy", ".35em")
        .text(d => d.name);
    node.exit().remove();

    link = link.data(links);
}

function onClick(clicked_node) {
    console.log("click!");
    console.log(clicked_node);
    var new_node = { name: "Test", group: 2 };
    nodes.push(new_node);
    // Has x & y set to NaN after adding
    links.push({ source: clicked_node, target: new_node });
    restart();
}

当我单击第一个节点时,导致该节点

{ name: "Test", group: 2 };

要添加的内容,D3会引发错误

node.attr("transform", d => {
    // d.x & d.y are NaN for new nodes
    return "translate(" + d.x + "," + d.y + ")";
});

因为此新节点的d.xd.yNaN

我尝试明确设置它们:

{ name: "Test", group: 2, x: clicked_node.y, y: clicked_node.y };

但是我得到了同样的错误。在检查器中,将此节点添加到屏幕后,xy的值将变成pxpy的值!

我不明白为什么会这样。

1 个答案:

答案 0 :(得分:2)

您缺少一行代码。添加新节点后,您需要重新启动d3的仿真以计算其位置:

function restart() {
    node = node.data(nodes);

    node.enter().append("g")
        .attr("class", "node")
        .call(force.drag);
    node.append("circle")
        .attr("r", 10)
        .on("mousedown", onClick);
    node.append("text")
        .attr("dx", 12)
        .attr("dy", ".35em")
        .text(d => d.name);
    node.exit().remove();

    link = link.data(links);

    force.start(); //<-- start simulation
}

运行代码:

<!DOCTYPE html>
<html>

  <head>
    <script data-require="d3@3.5.17" data-semver="3.5.17" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.js"></script>
  </head>

  <body>
    <script>
      var width = 960;
var height = 500;

var force = d3.layout.force()
    .gravity(0.05)
    .distance(100)
    .charge(-100)
    .size([width, height])
    .nodes([{ "name": "One", "group": 1 }])
    .start();

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

var nodes = force.nodes();
var links = force.links();

var link = svg.selectAll(".link")
    .data(links)
    .enter().append("line")
    .attr("class", "link");

var node = svg.selectAll(".node")
    .data(force.nodes())
    .enter().append("g")
    .attr("class", "node")
    .call(force.drag);

node.append("circle")
    .attr("r", 10)
    .on("mousedown", onClick);

node.append("text")
    .attr("dx", 12)
    .attr("dy", ".35em")
    .text(d => d.name);

force.on("tick", function() {
    link.attr("x1", d => d.source.x)
        .attr("y1", d => d.source.y)
        .attr("x2", d => d.target.x)
        .attr("y2", d => d.target.y);

    node.attr("transform", d => {
        // d.x & d.y are NaN for new nodes
        return "translate(" + d.x + "," + d.y + ")";
    });
});

restart();

function restart() {
    node = node.data(nodes);

    node.enter().append("g")
        .attr("class", "node")
        .call(force.drag);
    node.append("circle")
        .attr("r", 10)
        .on("mousedown", onClick);
    node.append("text")
        .attr("dx", 12)
        .attr("dy", ".35em")
        .text(d => d.name);
    node.exit().remove();

    link = link.data(links);
    
    force.start();
}

function onClick(clicked_node) {
    console.log("click!");
    console.log(clicked_node);
    var new_node = { name: "Test", group: 2 };
    nodes.push(new_node);
    // Has x & y set to NaN after adding
    links.push({ source: clicked_node, target: new_node });
    restart();
}
    </script>
  </body>

</html>