通过鼠标单击向强制有向图中的两个节点添加链接的问题

时间:2017-10-31 00:03:32

标签: javascript d3.js

我正在尝试创建一个交互式图表。为此,我想点击两个节点并创建一个链接(稍后我想删除一个链接,如果它存在)。

我跟随迈克博斯托克的例子: https://bl.ocks.org/mbostock/1095795

我添加了这个功能:

function changeLink() { 

  if (nodeobjList.length < 1) {
    nodeobjList.push(nodeobj)
  } else if (nodeobjList.length == 1) {
    nodeobjList.push(nodeobj)

    firstnode = nodeobjList[0]
    secondnode = nodeobjList[1]

    graphLinks.push({source: firstnode.id, target: secondnode.id, value: 1 });
    console.log(graphLinks)

    // Apply the general update pattern to the links.
    link = link.data(graphLinks, function(d) { return firstnode.id + "-" + secondnode.id; });
    link.exit().remove();
    link = link.enter().append("line").merge(link);

 // Update and restart the simulation.
 simulation.nodes(nodes);
 simulation.force("link").links(link);
 simulation.alpha(1).restart();

    }
}

这会产生错误:

"Uncaught TypeError: Cannot create property 'vx' on string 'MotherPlutarch'"

所以我用对象替换了id,如:

graphLinks.push({source: firstnode, target: secondnode, value: 1 });

这次图表的所有点都移动到左上角。 错误是:

Error: <line> attribute x1: Expected length, "NaN".

我可以通过鼠标位置获取行的开头和结尾的x,y坐标,或者通过id过滤节点。但由于节点都在向左上角移动,我怀疑一些更复杂的错误或缺失。

有人知道发生了什么吗? 提前谢谢了, 马库斯

有关详细信息,请在此处添加完整的html文件:

<!DOCTYPE html>
<meta charset="utf-8">
<style>

.links line {
  stroke: #999;
  stroke-opacity: 0.6;
}

.nodes circle {
  stroke: #fff;
  stroke-width: 1.5px;
}

</style>
<svg width="960" height="600"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>

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

var color = d3.scaleOrdinal(d3.schemeCategory20);

var defs = svg.append('defs');
defs.append("pattern")
    .attr("x", 0)
    .attr("y", 0)
    .attr("width", 1)
    .attr("height", 1)
    .attr("id", "test")
    .append("image")
    .attr("xlink:href", 'https://assets-cdn.github.com/images/modules/open_graph/github-octocat.png')
    .attr("width", 100)
    .attr("height", 60)
    .attr("x", -30)
    .attr("y", -10);

var simulation = d3.forceSimulation()
    .force("link", d3.forceLink().id(function(d) { return d.id; }))
    .force("charge", d3.forceManyBody())
    .force("center", d3.forceCenter(width / 2, height / 2));

var node;
var link;
var graphNodes;
var graphLinks;
var nodeobjList = [];
var nodeobj;

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

 // build the arrow.
  svg.append("svg:defs").selectAll("marker")
        .data(["end"])                    // Different link/path types can be defined here
        .enter().append("svg:marker")    // This section adds in the arrows
        .attr("id", String)
        .attr("viewBox", "0 -5 10 10")
        .attr("refX", 13)
        .attr("refY", 0)
        .attr("markerWidth", 9)
        .attr("markerHeight", 9)
        .attr("orient", "auto")
        .attr("xoverflow",'visible')
        .append("svg:path")
        .attr("d", "M0,-5L10,0L0,5")
        .attr('fill', '#999')
        .style('stroke','none');

  link = svg.append("g")
      .attr("class", "links")
      .selectAll("line")
      .data(graph.links)
      .enter().append("line")
      .attr("stroke-width", function(d) { return Math.sqrt(d.value); })
      .attr("marker-end", "url(#end)");

  graphLinks = graph.links
  graphNodes = graph.nodes

  node = svg.append("g")
      .attr("class", "nodes")
      .selectAll("circle")
      .data(graph.nodes)
      .enter().append("circle")
      .attr("r", 20)
      .on("click", function(d){
        d3.select(this).attr("fill", "url(#test)")
      })
      .on("dblclick", function(d){
         nodeobj = d
         changeLink();
      })
      .call(d3.drag()
          .on("start", dragstarted)
          .on("drag", dragged)
          .on("end", dragended))

  node.append("title")
      .text(function(d) { return d.id; });

  simulation
      .nodes(graph.nodes)
      .on("tick", ticked);

  simulation.force("link")
      .links(graph.links);

  function ticked() {
    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 dragstarted(d) {
  if (!d3.event.active) simulation.alphaTarget(0.3).restart();
  d.fx = d.x;
  d.fy = d.y;
}

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

function dragended(d) {
  if (!d3.event.active) simulation.alphaTarget(0);
  d.fx = null;
  d.fy = null;
}

function changeLink() { 

    if (nodeobjList.length < 1) {
        nodeobjList.push(nodeobj)
    } else if (nodeobjList.length == 1) {
        nodeobjList.push(nodeobj)

        firstnode = nodeobjList[0]
        secondnode = nodeobjList[1]

        graphLinks.push({source: firstnode, target: secondnode, value: 1 });

    console.log(graphLinks)

        // Apply the general update pattern to the links.
        link = link.data(graphLinks, function(d) { return firstnode + "-" + secondnode; });
        link.exit().remove();
        link = link.enter().append("line").merge(link);

        // Update and restart the simulation.
        simulation.nodes(nodes);
        simulation.force("link").links(link);
        simulation.alpha(1).restart();

    }
}
</script>`

1 个答案:

答案 0 :(得分:0)

这里有很多错误

  1. 更新模拟时引用错误的数组

    simulation.nodes(节点); simulation.force( “链接”)的链路(链路);

  2. 节点和链接都应该引用graphNodes和graphLinks数组

    1. 您应该在创建新链接后清除nodeobjList数组,例如

      nodeobjList = []

    2. 例如,您更新的代码可能如下所示,这更像是您引用的Bostock示例:

      function changeLink() { 
      
          if (nodeobjList.length < 1) {
              nodeobjList.push(nodeobj)
          } else if (nodeobjList.length == 1) {
              nodeobjList.push(nodeobj)
              firstnode = nodeobjList[0].id
              secondnode = nodeobjList[1].id
      
              graphLinks.push({source: firstnode, target: secondnode, value: 1 });
      
              nodeobjList = []
              restart();
      
          }
      }
      
      
       function restart() {
              link = link.data(graphLinks, function(d) { return firstnode + "-" + secondnode; });
              link.exit().remove();
              link = link.enter().append("line").merge(link);
      
              // Update and restart the simulation.
              simulation.force("link").links(graphLinks);
              simulation.alpha(1).restart();
       }