添加到d3js强制布局的链接会导致节点

时间:2015-06-23 21:48:01

标签: javascript d3.js

我正在开发一个d3js侧项目,我正在使用强制布局来显示节点和线来表示图形。我已经布置了代码,以便可以动态更新图形。我这样做是通过:

  1. 清除force.nodes()然后强制.links()
  2. 将我要添加的所有节点推送到force.nodes()
  3. 更新图表(加入,输入,更新,退出)
  4. 将我想要的所有链接推送到force.nodes()中的节点以强制链接
  5. 更新图表(加入,输入,更新,退出)
  6. 只要我只有节点可以显示,这就可以工作,但是一旦我尝试将链接推送到force.links,那么所有地狱都会松动,两个节点最终会隐藏在左上角。

    查看DOM我可以看到以下内容:

    DOM

    如您所见,transform / translate参数包含NaN值。所以有些东西在我的脸上爆炸,但经过两天的虫子狩猎后,我怀疑我在这里遗失了一些东西,需要洗个冷水澡。

    我已经将我的代码剥离到可以重现错误的最小集合。请注意,节点显示正常,直到链接被推入force.links。没有链接被绘制,只有节点,但推送它所属的链接的行为正在破坏节点中的数据。这种更新图表的方式应该按照我见过的例子工作。

    d3.json("data/fm.json", function(error, graph) {
        if (error) throw error;
        function chart(elementName) {
    
            // look for the node in the d3 layout
            var findNode = function(name) {
                for (var i in nodes) {
                    if (nodes[i]["name"] === name) return nodes[i];
                };
            };           
    
            var width = 960, // default width
                height = 450, // default height
                color = d3.scale.category10(),
                force = d3.layout.force(),
                nodes = force.nodes(),
                links = force.links(),
                vis,
                runOnceFlag = true;
    
            vis = d3.select(elementName)
                    .append("svg:svg")
                    .attr("width", width)
                    .attr("height", height)
                    .attr("id", "svg")
                    .attr("pointer-events", "all")
                    .attr("viewBox", "0 0 " + width + " " + height)
                    .attr("perserveAspectRatio", "xMinYMid")
                    .append('svg:g');            
    
            var update = function() {               
    
                var node = vis.selectAll("g.node")
                        .data(nodes, function (d) {
                            return d.name;
                        });
    
                var nodeEnter = node.enter().append("g")
                        .attr("class", "node")
                        .call(force.drag);
    
                nodeEnter.append("svg:circle")
                        .attr("r", 12)
                        .attr("id", function (d) {
                            return "Node;" + d.name;
                        })
                        .attr("class", "nodeStrokeClass")
                        .attr("fill", function(d) { return color(d.group); });
    
                nodeEnter.append("svg:text")
                        .attr("class", "textClass")
                        .attr("x", 14)
                        .attr("y", ".31em")
                        .text(function (d) {
                            return d.name;
                        });
    
                node.exit().remove();
    
                force.on("tick", function () {
    
                    node.attr("transform", function (d) {
                        console.log(d);
                        return "translate(" + d.x + "," + d.y + ")";
                    }); 
    
                });
    
                // Restart the force layout.
                force
                    .charge(-120)
                    .linkDistance( function(d) { return d.value * 10 } )
                    .size([width, height])
                    .start();
            };
    
            var a = graph.nodes[0];
            var b = graph.nodes[1]
            nodes.push(a);
            update();
            nodes.push(b);
            update();
            var c = {"source": findNode('a'), "target": findNode('b')}
            // the line below causes the error
            links.push(c);
            update()           
        };
        ///
        chart('body');
    });
    

    这是我的数据:

    {
        "nodes":[
          {"name":"a", "group":1},
          {"name":"b", "group":2},
          {"name":"c", "group":3},
          {"name":"d", "group":4},
          {"name":"e", "group":5},
          {"name":"f", "group":6},
          {"name":"g", "group":7},
          {"name":"h", "group":1},
          {"name":"i", "group":2},
          {"name":"j", "group":3},
          {"name":"k", "group":4},
          {"name":"l", "group":5},
          {"name":"m", "group":6},
          {"name":"n", "group":7}
      ],
        "links":[
          {"source":0,"target":1,"value":1},
          {"source":2,"target":3,"value":1},
          {"source":4,"target":5,"value":1},
          {"source":7,"target":8,"value":1},
          {"source":9,"target":10,"value":1},
          {"source":11,"target":12,"value":1},
          {"source":0,"target":5,"value":1},
          {"source":1,"target":5,"value":1},
          {"source":0,"target":6,"value":1},
          {"source":1,"target":6,"value":1},
          {"source":0,"target":7,"value":1},
          {"source":1,"target":7,"value":1},
          {"source":2,"target":8,"value":1},
          {"source":3,"target":8,"value":1},
          {"source":2,"target":9,"value":1},
          {"source":3,"target":9,"value":1},
          {"source":4,"target":11,"value":1},
          {"source":5,"target":11,"value":1},
          {"source":9,"target":12,"value":1},
          {"source":10,"target":12,"value":1},
          {"source":11,"target":13,"value":1},
          {"source":12,"target":13,"value":1}
        ]
      }
    

1 个答案:

答案 0 :(得分:2)

您的代码存在一些基本问题,请参阅下面的corrected concept ...

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title></title>
  <style>
    .link {
      stroke: #2E2E2E;
      stroke-width: 2px;
    }

    .node {
      stroke: #fff;
      stroke-width: 2px;
    }
    .textClass {
      stroke: #323232;
      font-family: "Lucida Grande", "Droid Sans", Arial, Helvetica, sans-serif;
      font-weight: normal;
      stroke-width: .5;
      font-size: 14px;
    }
  </style>

</head>
<body>
<!--<script src="d3 CB.js"></script>-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script>
  d3.json("data.json", function (error, graph) {
    if (error) throw error;
    function chart(elementName) {

      // look for the node in the d3 layout
      var findNode = function (name) {
        for (var i in nodes) {
          if (nodes[i]["name"] === name) return nodes[i];
        }
      };

      var width = 960, // default width
        height = 450, // default height
        color = d3.scale.category10(),
        nodes = graph.nodes,
        links = graph.links,
        force = d3.layout.force()
          .nodes(nodes)
          .links([]),
        vis,
        runOnceFlag = true;

      vis = d3.select(elementName)
        .append("svg:svg")
        .attr("width", width)
        .attr("height", height)
        .attr("id", "svg")
        .attr("pointer-events", "all")
        .attr("viewBox", "0 0 " + width + " " + height)
        .attr("perserveAspectRatio", "xMinYMid")
        .append('svg:g');

      var update = function () {
        var link = vis.selectAll("line")
          .data(force.links(), function (d) {
            return d.source + "-" + d.target;
          });

        link.enter().insert("line", "g")
          .attr("id", function (d) {
            return d.source + "-" + d.target;
          })
          .attr("stroke-width", function (d) {
            return d.value / 10;
          })
          .attr("class", "link")
          .style("stroke", "red")
          .transition().duration(5000).style("stroke", "black");
        link.append("title")
          .text(function (d) {
            return d.value;
          });
        link.exit().remove();

        var node = vis.selectAll("g.node")
          .data(nodes, function (d) {
            return d.name;
          });

        var nodeEnter = node.enter().append("g")
          .attr("class", "node")
          .call(force.drag);

        nodeEnter.append("svg:circle")
          .attr("r", 12)
          .attr("id", function (d) {
            return "Node;" + d.name;
          })
          .attr("class", "nodeStrokeClass")
          .attr("fill", function (d) {
            return color(d.group);
          });

        nodeEnter.append("svg:text")
          .attr("class", "textClass")
          .attr("x", 14)
          .attr("y", ".31em")
          .text(function (d) {
            return d.name;
          });

        node.exit().remove();

        force.on("tick", function () {

          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("transform", function (d) {
            console.log(d);
            return "translate(" + d.x + "," + d.y + ")";
          });

        });

        // Restart the force layout.
        force
          .charge(-120)
          .linkDistance(function (d) {
            return d.value * 100
          })
          .size([width, height])
          .start();
      };

      update();
      var c = {"source": findNode('a'), "target": findNode('b'), value: 1}
      // the line below causes the error
      window.setTimeout(function() {
        force.links().push(c);
        update()
      },2000)
    };
    //
    chart('body');
  });
</script>
</body>
</html>