D3.js(V4)用组作为节点修改Force Directed Graph

时间:2017-06-09 23:18:57

标签: javascript d3.js graph

我已成功使用D3 V4创建了力布局图,现在我正在尝试添加功能,当您单击某个节点时,它将从图中删除,而不必每次都重绘图形。我正在尝试关注new general update pattern,当我的节点只是圆圈时,我可以开始工作,但在我的图表中,节点是包含圆圈和两个标签的组。当我尝试对节点进行分组并应用常规更新模式时,我收到strange behavior (JSFiddle)

我是D3的新手,我认为我误解了有关进入和退出声明的事情,或者应该做的事情的顺序。如果你们中的任何一个人可以看看我的构建图功能并让我知道什么事情,那就太棒了。

代码(原谅格式化,JSFiddle需要)

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>GraphViz</title>
</head>

<body name="top" class="container">
    <div class="graphPanel" style="height: 800px">
        <svg width="100%" height="100%"></svg>
    </div>
    <script
  src="https://code.jquery.com/jquery-3.2.1.min.js"
  integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
  crossorigin="anonymous"></script>
    <script
    type="text/javascript"
    src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min.js"></script>
    <script>
        data = {
  "nodes": [
    {
        "type": "User ID",
        "detail": "Bob Smith",
        "data": "7d7c...1e1c",
        "id": "0"
    },
    {
        "type": "Device",
        "detail": "MacOS",
        "data": "6334...1e1c",
        "id": "1"
    },
    {
        "type": "Device",
        "detail": "Windows",
        "data": "185c...1e1c",
        "id": "2"
    },
    {
        "type": "Device",
        "detail": "Windows",
        "data": "6334...1e1c",
        "id": "3"
    },
    {
        "type": "Device",
        "detail": "Android",
        "data": "6334...1e1c",
        "id": "4"
    },
    {
        "type": "Device",
        "detail": "iOS Browser",
        "data": "2312...1e1c",
        "id": "5"
    },
    {
        "type": "Shipping Address",
        "detail": "",
        "data": "San Jose, CA 95113",
        "id": "6"
    },
    {
        "type": "Account Name",
        "detail": "",
        "data": "4d6f....aa10",
        "id": "7"
    },
    {
        "type": "Account Login",
        "detail": "",
        "data": "bsmith",
        "id": "8"
    },
    {
        "type": "Credit Card Hash",
        "detail": "",
        "data": "1cca...81e1",
        "id": "9"
    },
    {
        "type": "SSN",
        "detail": "",
        "data": "4fed...3er5",
        "id": "10"
    },
    {
        "type": "Account Number",
        "detail": "",
        "data": "78945648",
        "id": "11"
    },
    {
        "type": "Email",
        "detail": "",
        "data": "bob@bobsmith.com",
        "id": "12"
    }
  ],
  "links": [
    {
        "source": "0",
        "target": "1",
        "id": "0"
    },
    {
        "source": "0",
        "target": "2",
        "id": "1"
    },
    {
        "source": "0",
        "target": "3",
        "id": "2"
    },
    {
        "source": "0",
        "target": "4",
        "id": "3"
    },
    {
        "source": "0",
        "target": "5",
        "id": "4"
    },
    {
        "source": "0",
        "target": "6",
        "id": "5"
    },
    {
        "source": "0",
        "target": "7",
        "id": "6"
    },
    {
        "source": "0",
        "target": "8",
        "id": "7"
    },
    {
        "source": "0",
        "target": "9",
        "id": "8"
    },
    {
        "source": "0",
        "target": "10",
        "id": "9"
    },
    {
        "source": "0",
        "target": "11",
        "id": "10"
    },
    {
        "source": "0",
        "target": "12",
        "id": "11"
    }
  ]
}

    buildGraph(data.links, data.nodes);

  function buildGraph(links, nodes) {
    var svg = d3.select("svg"),
    width = $("svg").width(),
    height = $("svg").height(),
    color = d3.scaleOrdinal(d3.schemeCategory10);

    var simulation = d3.forceSimulation(nodes)
    .force("charge", d3.forceManyBody().strength(-1000))
    .force("link", d3.forceLink(links).distance(200))
    .force("x", d3.forceX())
    .force("y", d3.forceY())
    .alphaTarget(1)
    .on("tick", ticked);

    var g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"),
    link = g.append("g").attr("stroke", "#000").attr("stroke-width", 1.5).selectAll(".link"),
    node = g.append("g").attr("stroke", "#000").attr("stroke-width", 1.5).selectAll(".node");

    var circle, label, data;

    restart();

    function restart() {

      node = node.data(nodes, function(d) { return d.id;});
      node.exit().remove();
      node = node.enter().append("g")
      .merge(node);

      circle = node.append("circle")
      .attr("fill", function(d) { return color(d.id); }).attr("r", 8)
      .attr("id", function(d){return d.id;})
      .call(d3.drag()
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended));

      link = link.data(links, function(d) { return d.source.id + "-" + d.target.id; });
      link.exit().remove();
      link = link.enter().append("line")
      .attr("id", function(d) {
        return d.source + "-" + d.target;
      })
      .merge(link);

      label = node.append("text")
      .attr("font-size", "15")
      .attr("font-weight", "bold")
      .attr("class", "nodeLabel")
      .html(function(d) { 
        var detail = d.detail === "" ? "" : ": " + d.detail;
        return d.type + detail;
      });

      data = node.append("text")
      .html(function(d) { 
        return d.data;
      })
      .attr("class", "nodeLabel");

      simulation.nodes(nodes);
      simulation.force("link").links(links);
      simulation.alpha(1).restart();
    }

    $("circle").click(function() {
      console.log(links)
      $("circle").removeClass("selected");
      d3.select(this).classed("selected", true);
      var id = $(this).attr("id");
      nodes.forEach(function(thisNode) {
        if (thisNode.id === id) {
          var index = nodes.indexOf(thisNode);
          nodes.splice(index, 1);
        }
      });
      for(var i = links.length - 1; i >=0; i--) {
        if(links[i].target.id === id || links[i].source.id === id) {
          links.splice(i, 1);
        }
      }
      restart();
    });

    function ticked() {
      node.attr("cx", function(d) { return d.x; })
      .attr("cy", function(d) { return 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; });

      circle
      .attr("cx", function(d) { return d.x; })
      .attr("cy", function(d) { return d.y; });

      label
      .attr("x", function(d) { return d.typeCode === "did" ? d.x + 35 : d.x + 25; })
      .attr("y", function(d) { return d.y; });

      data
      .attr("x", function(d) { return d.typeCode === "did" ? d.x + 35 : d.x + 25; })
      .attr("y", function(d) { return d.y + 15; });
    }

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

    </script>
</body>

</html>

1 个答案:

答案 0 :(得分:0)

我改变了之前的答案,因为它完全错了,请看这个链接:enter link description here ,这应该让你一切顺利。我仍然保留关于你将删除事件绑定到圈子的意见,你必须将它绑定到你的节点()