【问题标题】:How to create d3.js Collapsible force layout with non tree data?如何使用非树数据创建 d3.js 可折叠强制布局?
【发布时间】:2015-08-10 08:31:32
【问题描述】:

我有一个 d3 强制定向布局,其数据在下面的类似结构中。是否可以对其应用http://bl.ocks.org/mbostock/1062288 等可折叠力布局?我想要一个节点在点击时折叠/展开。

{
  "nodes": [
    {"x": 469, "y": 410},
    {"x": 493, "y": 364},
    {"x": 442, "y": 365},
    {"x": 467, "y": 314},
  ],
  "links": [
    {"source":  0, "target":  1},
    {"source":  1, "target":  2},
    {"source":  2, "target":  0},
    {"source":  1, "target":  3},
    {"source":  3, "target":  2},
  ]
}

【问题讨论】:

    标签: d3.js tree data-visualization force-layout directed-graph


    【解决方案1】:

    如果我理解正确,也许这就是您要寻找的。我编辑了您链接到的演示。现在,当源节点折叠时,我们遍历所有边并寻找它有边的其他节点。

    对于源节点有边的每个目标节点,我们增加它的折叠计数。如果节点的折叠计数大于零,则不会显示。

    当我们展开一个节点时,我们做同样的事情,除了我们从折叠计数中减少。

    我们需要这个折叠计数,因为我们不在树中,节点可以有多个节点,这会导致它们折叠。

    我为有向图制作了这项工作,但我不确定这是否是您想要的。

    让我知道你的想法!

    我使用的json:

      {
        "nodes": [
         {"x": 469, "y": 410},
         {"x": 493, "y": 364},
         {"x": 442, "y": 365},
         {"x": 467, "y": 314}
     ],
         "links": [
          {"source":  0, "target":  1},
          {"source":  1, "target":  2},
          {"source":  2, "target":  0},
          {"source":  1, "target":  3},
          {"source":  3, "target":  2}
      ]
     }
    

    修改教程代码:

    <!DOCTYPE html>
    <meta charset="utf-8">
    <title>Force-Directed Graph</title>
    <style>
    
    .node {
      cursor: pointer;
      stroke: #3182bd;
      stroke-width: 1.5px;
    }
    
    .link {
      fill: none;
      stroke: #9ecae1;
      stroke-width: 1.5px;
    }
    
    </style>
    <body>
    <script src="http://d3js.org/d3.v3.min.js"></script>
    <script>
    
    var width = 960,
        height = 500,
        root;
    
    var force = d3.layout.force()
        .size([width, height])
        .on("tick", tick);
    
    var svg = d3.select("body").append("svg")
        .attr("width", width)
        .attr("height", height);
    
    //Added markers to indicate that this is a directed graph
    svg.append("defs").selectAll("marker")
        .data(["arrow"])
        .enter().append("marker")
        .attr("id", function(d) { return d; })
        .attr("viewBox", "0 -5 10 10")
        .attr("refX", 15)
        .attr("refY", -1.5)
        .attr("markerWidth", 4)
        .attr("markerHeight", 4)
        .attr("orient", "auto")
        .append("path")
        .attr("d", "M0,-5L10,0L0,5");
    
    var link = svg.selectAll(".link"),
        node = svg.selectAll(".node");
    
    d3.json("graph.json", function(json) {
      root = json;
      //Give nodes ids and initialize variables
      for(var i=0; i<root.nodes.length; i++) {
        var node = root.nodes[i];
        node.id = i;
        node.collapsing = 0;
        node.collapsed = false;
      }
      //Give links ids and initialize variables
      for(var i=0; i<root.links.length; i++) {
        var link = root.links[i];
        link.source = root.nodes[link.source];
        link.target = root.nodes[link.target];
        link.id = i;
      }
    
      update();
    });
    
    function update() {
      //Keep only the visible nodes
      var nodes = root.nodes.filter(function(d) {
        return d.collapsing == 0;
      });
      var links = root.links;
      //Keep only the visible links
      links = root.links.filter(function(d) {
        return d.source.collapsing == 0 && d.target.collapsing == 0;
      });
    
      force
          .nodes(nodes)
          .links(links)
          .start();
    
      // Update the links…
      link = link.data(links, function(d) { return d.id; });
    
      // Exit any old links.
      link.exit().remove();
    
      // Enter any new links.
      link.enter().insert("line", ".node")
          .attr("class", "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; })
          .attr("marker-end", "url(#arrow)");
    
      // Update the nodes…
      node = node.data(nodes, function(d){ return d.id; }).style("fill", color);
    
      // Exit any old nodes.
      node.exit().remove();
    
      // Enter any new nodes.
      node.enter().append("circle")
          .attr("class", "node")
          .attr("cx", function(d) { return d.x; })
          .attr("cy", function(d) { return d.y; })
          .attr("r", function(d) { return Math.sqrt(d.size) / 10 || 4.5; })
          .style("fill", color)
          .on("click", click)
          .call(force.drag);
    }
    
    function tick() {
      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; });
    }
    
    // Color leaf nodes orange, and packages white or blue.
    function color(d) {
      return d.collapsed ? "#3182bd" : d.children ? "#c6dbef" : "#fd8d3c";
    }
    
    // Toggle children on click.
    function click(d) {
      if (!d3.event.defaultPrevented) {
        //check if link is from this node, and if so, collapse
        root.links.forEach(function(l) {
          if(l.source.id == d.id) {
            if(d.collapsed){
              l.target.collapsing--;
            } else {
              l.target.collapsing++;
            }
          }
        });
        d.collapsed = !d.collapsed;
      }
      update();
    }
    
    </script>
    

    【讨论】:

    【解决方案2】:

    试试这个:

        var width = 960,height = 500;
    
        var force = d3.layout.force().size([width, height]).charge(-400)
                    .linkDistance(40)
                    .on("tick", tick);
    
             var drag = force.drag().on("dragstart", dragstart);
    
               var svg = d3.select("body").append("svg").attr("width", width)
                            .attr("height", height);
    
               var link = svg.selectAll(".link"),
                      node = svg.selectAll(".node");
    
                d3.json("graph.json", function(error, graph) {
                         force.nodes(graph.nodes).links(graph.links)
                             .start();
    
            link = link.data(graph.links).enter().append("line")
                          .attr("class", "link");
    
                      node = node.data(graph.nodes)
                     .enter().append("circle")
                     .attr("class", "node")
                     .attr("r", 12)
                     .call(drag);
             });
    
             function tick() {
                  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 dragstart(d) {
                      d3.select(this).classed("fixed", d.fixed = true);
                  }
    

    你应该像这样使用 json 文件:

    graph.json

          {
            "nodes": [
             {"x": 469, "y": 410},
             {"x": 493, "y": 364},
             {"x": 442, "y": 365},
             {"x": 467, "y": 314},
         ],
             "links": [
              {"source":  0, "target":  1},
              {"source":  1, "target":  2},
              {"source":  2, "target":  0},
              {"source":  1, "target":  3},
              {"source":  3, "target":  2},
          ]
         }
    

    【讨论】:

    猜你喜欢
    • 2015-03-10
    • 2013-03-10
    • 2016-04-23
    • 1970-01-01
    • 2023-03-20
    • 2016-04-15
    • 1970-01-01
    • 2023-04-06
    • 1970-01-01
    相关资源
    最近更新 更多