3d.js - 更新力导向图上的节点

3d.js - Updating nodes on a force directed graph

我目前正在开发一个程序来创建图形(顶点 + 边)并应用算法。我选择了 3d.js 来制作这个程序。 添加顶点时,我遇到了有关 repaint 函数的问题。

当我添加第一个顶点时,它是有效的,但对于接下来的顶点,我的顶点迅速增加(顶点和标签)...

截图:



源代码:


var graph = (function() {
  return {
    modules: {},
    nodes: [],
    links: []
  }
})();

graph.modules.engin = (function() {
  var w, h, circleWidth, svgApp, force, link, node, nodes;
  var palette = {
    "white": "#FFFFFF",
    "gray": "#708284",
    "orange": "#BD3613",
    "red": "#D11C24",
    "blue": "#2176C7"
  };
  return {
    init: function() {
      var w = 960,
        h = 450;

      circleWidth = 7;

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

      force = d3.layout.force()
        .nodes(graph.nodes)
        .links([])
        .gravity(0.1)
        .charge(-1000)
        .size([w, h]);


      //console.log(nodes);
      //nodes.push({})
      graph.modules.engin.repaint();

      graph.modules.engin.insertNode('V1');
      graph.modules.engin.insertNode('V2');
      graph.modules.engin.insertNode('V3');
    },
    repaint: function() {
      console.log('update');

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

      link = svgApp.selectAll(".link")
        .data(links);
      link.enter().append("line")
        .attr("class", "link")
        .attr("stroke", palette.gray)
        .attr("fill", "none");
      link.exit().remove();


      node = svgApp.selectAll("circle.node")
        .data(nodes);
      node.enter().append("g")
        .attr("class", "node")
        .on("mouseover", function(d, i) { //MOUSEOVER
          if (!d.root) {
            d3.select(this).selectAll("circle")
              .transition()
              .duration(250)
              .style("cursor", "none")
              .attr("r", circleWidth + 3)
              .attr("fill", palette.orange);

            d3.select(this).select("text")
              .transition()
              .style("cursor", "none")
              .duration(250)
              .style("cursor", "none")
              .attr("font-size", "1.5em")
              .attr("x", 15)
              .attr("y", 5)
          } else {
            d3.select(this).selectAll("circle")
              .style("cursor", "none");

            d3.select(this).select("text")
              .style("cursor", "none");
          }
        })
        .on("mouseout", function(d, i) { //MOUSEOUT
          if (!d.root) {
            //CIRCLE
            d3.select(this).selectAll("circle")
              .transition()
              .duration(250)
              .attr("r", circleWidth)
              .attr("fill", palette.blue);

            //TEXT
            d3.select(this).select("text")
              .transition()
              .duration(250)
              .attr("font-size", "1em")
              .attr("x", 8)
              .attr("y", 4)
          }
        })
        .call(force.drag);

      node.append("svg:circle")
        .attr("cx", function(d) {
          return d.x;
        })
        .attr("cy", function(d) {
          return d.y;
        })
        .attr("r", circleWidth)
        .attr("fill", function(d, i) {
          if (!d.root) {
            return palette.blue;
          } else {
            return palette.red
          }
        });

      node.append("text")
        .text(function(d, i) {
          return d.name;
        })
        .attr("x", function(d, i) {
          return circleWidth + 5;
        })
        .attr("y", function(d, i) {
          if (!d.root) {
            return circleWidth
          } else {
            return circleWidth + 5
          }
        })
        .attr("font-family", "Bree Serif")
        .attr("fill", function(d, i) {
          return palette.white;
        })
        .attr("font-size", function(d, i) {
          return "1em";
        })
        .attr("text-anchor", function(d, i) {
          if (!d.root) {
            return "beginning";
          } else {
            return "end"
          }
        });
      node.exit().remove();

      force.on("tick", function(e) {
        node.attr("transform", function(d, i) {
          return "translate(" + d.x + "," + d.y + ")";
        });

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

      force.start();
    },
    insertNode: function(name) {
      nodes.push({
        name: name
      })
      graph.modules.engin.repaint();
    }
  }
})();
$(document).ready(function() {
  graph.modules.engin.init();
});
html {
  background-color: #042029;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>

<button onclick="addNodes()">Restart Animation</button>

谢谢, YM

我解决了我的问题:变量引用错误和选择器元素:

源代码:

var graph = (function() {
  return {
    modules: {},
    nodes: [],
    links: []
  }
})();

graph.modules.engin = (function() {
  var w, h, circleWidth, svgApp, force;
  var palette = {
    "white": "#FFFFFF",
    "gray": "#708284",
    "orange": "#BD3613",
    "red": "#D11C24",
    "blue": "#2176C7"
  };
  return {
    init: function() {
      h = $('body').height();
      w = $('body').width();

      circleWidth = 7;

      svgApp = d3.select("body")
        .append("svg:svg")
        .attr("pointer-events", "all")
        .attr("width", w)
        .attr("height", h)
        .attr("viewBox", "0 0 " + w + " " + h)
        .attr("perserveAspectRatio", "xMinYMid");

      force = d3.layout.force();

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

      graph.modules.engin.repaint();

      graph.modules.engin.insertNode('V1');
      graph.modules.engin.insertNode('V2');
      graph.modules.engin.insertNode('V3');
    },
    repaint: function() {
      console.log('update');

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

      console.log('BEFORE REPAINT');
      console.log(nodes);
      console.log(links);
      console.log(graph.nodes);
      console.log(graph.links);

      var link = svgApp.selectAll("line.link")
        .data(links);
      var linkEnter = link.enter().append("line")
        .attr("class", "link")
        .attr("stroke", palette.gray)
        .attr("fill", "none");
      link.exit().remove();


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

      var nodeEnter = node.enter().append("g")
        .attr("class", "node")
        .on("mouseover", function(d, i) {
          if (!d.root) {
            d3.select(this).select("circle")
              .transition()
              .duration(250)
              .style("cursor", "none")
              .attr("r", circleWidth + 3)
              .attr("fill", palette.yellow);

            d3.select(this) //.select("text")
              .transition()
              .style("cursor", "none")
              .duration(250)
              .style("cursor", "none")
              .attr("font-size", "1.5em")
              .attr("x", 15)
              .attr("y", 5)
          } else {
            d3.select(this).select("circle")
              .style("cursor", "none");

            d3.select(this).select("text")
              .style("cursor", "none");

          }
        })
        .on("mouseout", function(d, i) {
          if (!d.root) {
            d3.select(this).select("circle")
              .transition()
              .duration(250)
              .attr("r", circleWidth)
              .attr("fill", palette.blue);

            d3.select(this) //.select("text")
              .transition()
              .duration(250)
              .attr("font-size", "1em")
              .attr("x", 8)
              .attr("y", 4)
          }
        })
        .call(force.drag);

      nodeEnter.append("svg:circle")
        .attr("cx", function(d) {
          return d.x;
        })
        .attr("cy", function(d) {
          return d.y;
        })
        .attr("r", circleWidth)
        .attr("fill", function(d, i) {
          if (!d.root) {
            return palette.blue;
          } else {
            return palette.red
          }
        }).attr("id", function(d) {
          return "Node-" + d.name;
        });

      nodeEnter.append("svg:text")
        .text(function(d, i) {
          return d.name;
        })
        .attr("x", function(d, i) {
          return circleWidth + 5;
        })
        .attr("y", function(d, i) {
          if (!d.root) {
            return circleWidth - 2
          } else {
            return circleWidth + 5
          }
        })
        .attr("font-family", "Bree Serif")
        .attr("fill", function(d, i) {
          return palette.white;
        })
        .attr("font-size", function(d, i) {
          return "1em";
        })
        .attr("text-anchor", function(d, i) {
          if (!d.root) {
            return "beginning";
          } else {
            return "end"
          }
        });

      node.exit().remove();

      force.on("tick", function(e) {
        node.attr("transform", function(d, i) {
          return "translate(" + d.x + "," + d.y + ")";
        });

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

      force.gravity(0.1)
        .charge(-1000)
        .size([w, h])
        .start();
    },
    insertNode: function(name) {
      graph.nodes.push({
        name: name
      })
      graph.modules.engin.repaint();
    }
  }
})();
$(document).ready(function() {
  graph.modules.engin.init();
});
html {
  background-color: #042029;
}

body {
  height: 600px;
  width: 100%;
}
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>

<button onclick="addNodes()">Restart Animation</button>