D3.js径向可折叠桡骨树(每个分枝1个分枝)

时间:2016-10-12 12:33:41

标签: javascript css d3.js tree geometry

我正在制作Mike Bostock的collapsible tree的径向版本。

我希望每组分支都有一个分支,而不是每个元素分支。

这是我到目前为止所拥有的: enter image description here

我当前代表的JSFiddlehere

var flare中额外注释的行只是用于密度测试的额外行,用于查看大量信息的外观,如预期的那样。

白色圆圈用于隐藏根元素和白色背景上第一级的链接。要删除它,只需删除以下行:

var circle = svg.append("circle")
    .attr("cx", 0)
    .attr("cy", 0)
    .attr("r", radius - 5)
    .style("fill", "white");

以下是我想要的内容: enter image description here

从root到1级的工作原理无关紧要,只要1级元素形成一个圆(这里有3个1级元素),因为我仍然会将0级(根)隐藏到1级

但是,父元素的间距必须根据折叠的孩子而变化。

1 个答案:

答案 0 :(得分:3)

这是我对你的插图的看法。从本质上讲,它是一个非常自定义的路径生成器。我试图对此发表评论,如果您有任何疑问,请告诉我。

link.transition()
    .duration(duration)
    .attr("d", function(d) {

      // depth zero, don't draw, this is your "hidden" links
      if (d.source.depth === 0) return "";

      // if we have children
      if (d.source.children) {

        // sum the angles to find the midpoint of the children
        var pad = 20, //<-- pad is the step off distance to children
           sum = 0;
        d.source.children.forEach(function(c) {
          sum += c.x;
        });

        // this is the mid point position
        var ma = ((sum / d.source.children.length) - 90) * (Math.PI / 180),
          mr = d.source.children[0].y - pad,
          mid = [mr * Math.cos(ma), mr * Math.sin(ma)]; //x,y position

        // this is the source position
        var sa = (d.source.x - 90) * (Math.PI / 180),
          sr = d.source.y - pad,
          source = [sr * Math.cos(sa), sr * Math.sin(sa)];

        // this is the final target position
        var ta = (d.target.x - 90) * (Math.PI / 180),
          tr = d.target.y - pad,
          target = [tr * Math.cos(ta), tr * Math.sin(ta)];

        // this is the arc from mid to target
        var dx = target[0] - source[0],
          dy = target[1] - source[1],
          dr = Math.sqrt(dx * dx + dy * dy);

        // this is the line from the source, to the mid and arced to children
        return "M" + source +
          "L" + mid +
          "A" + dr + "," + dr + " 0 0," + (ma < ta ? 1 : 0) + " " + target[0] + "," + target[1];
      }
    });

更新了小提琴here

评论更新

检查此版本。它删除了我写的自定义弧,而是使用d3.svg.arc生成器。它还将绘图限制为仅删除冗余路径的第一个和最后一个子节点。

<!DOCTYPE html>
<html>

<head>
  <style>
    .node {
      cursor: pointer;
    }
    
    .node circle {
      fill: #fff;
      stroke: steelblue;
      stroke-width: 1.5px;
    }
    
    .node text {
      font: 10px sans-serif;
    }
    
    .link {
      fill: none;
      stroke: #ccc;
      stroke-width: 1.5px;
    }
    
    #svg {
      height: 500px;
      width: 500px;
    }
  </style>
</head>

<body>
  <script src="//d3js.org/d3.v3.min.js"></script>
  <div id="svg"></div>
  <script>
    flare = {
      "name": "root",
      "children": [{
          "name": "item1",
          "children": [{
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item2"
          }, {
            "name": "item3"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        },

        {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        }, {
          "name": "item4",
          "children": [{
            "name": "item5"
          }, {
            "name": "item6"
          }, {
            "name": "item7"
          }]
        },

        {
          "name": "item8",
          "children": [{
            "name": "item9"
          }, {
            "name": "item10"
          }]
        }
      ]
    };

    //variables used to modify some basic properties of the svg elements
    var divHeight = document.getElementById('svg').offsetHeight;
    var divWidth = document.getElementById('svg').clientWidth;
    var radius = 75;
    var separation = 2;

    var diameter = 800;

    var margin = {
        top: 20,
        right: 120,
        bottom: 20,
        left: 120
      },
      width = diameter,
      height = diameter;

    var i = 0,
      duration = 350,
      root;

    var tree = d3.layout.tree()
      .size([360, diameter / 2 - 80])
      .separation(function(a, b) {
        return (a.parent == b.parent ? 1 : separation) / a.depth;
      });
    //last line is the separation between branches of the tree when clicked

    var diagonal = d3.svg.line.radial();
    //    .projection(function(d) { var r = d.y, a = (d.x - 90) / 180 * Math.PI;
    //  return [r * Math.cos(a), r * Math.sin(a)]; });

    var svg = d3.select("#svg").append("svg")
      //.attr("width", width)
      //.attr("height", height)
      .attr("width", divWidth)
      .attr("height", divHeight)
      .call(d3.behavior.zoom().on("zoom", function() {
        svg.attr("transform", "translate(" + d3.event.translate[0] + "," + d3.event.translate[1] + ")" + " scale(" + d3.event.scale + ")")
      })).on("dblclick.zoom", null)
      .append("g")
      .attr("transform", "translate(" + divWidth / 2 + "," + divHeight / 2 + ")")
      .append("g");

    //alert("W = " + divWidth + ", H = " + divHeight);

    root = flare;
    root.x0 = height / 2;
    root.y0 = 0;

    root.children.forEach(collapse); // start with all children collapsed
    update(root);

    //create a circle in the center to remove root and first level of links
    var circle = svg.append("circle")
      .attr("cx", 0)
      .attr("cy", 0)
      .attr("r", radius - 5)
      .style("fill", "white");

    d3.select(self.frameElement).style("height", "800px");

    function update(source) {

      // Compute the new tree layout.
      var nodes = tree.nodes(root),
        links = tree.links(nodes);

      // Normalize for fixed-depth.
      nodes.forEach(function(d) {
        d.y = d.depth * radius;
      });

      // Update the nodes…
      var node = svg.selectAll("g.node")
        .data(nodes, function(d) {
          return d.id || (d.id = ++i);
        });

      // Enter any new nodes at the parent's previous position.
      var nodeEnter = node.enter().append("g")
        .attr("class", "node")
        //.attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")"; })
        .on("click", click);

      nodeEnter.append("circle")
        .attr("r", 1e-6)
        .style("fill", function(d) {
          return d._children ? "lightsteelblue" : "#fff";
        });

      nodeEnter.append("text")
        .attr("x", 10)
        .attr("dy", ".35em")
        .attr("text-anchor", "start")
        //.attr("transform", function(d) { return d.x < 180 ? "translate(0)" : "rotate(180)translate(-" + (d.name.length * 8.5)  + ")"; })
        .text(function(d) {
          return d.name;
        })
        .style("fill-opacity", 1e-6);

      // Transition nodes to their new position.
      var nodeUpdate = node.transition()
        .duration(duration)
        .attr("transform", function(d) {
          return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")";
        })

      nodeUpdate.select("circle")
        .attr("r", 4.5)
        .style("fill", function(d) {
          return d._children ? "lightsteelblue" : "#fff";
        });

      nodeUpdate.select("text")
        .style("fill-opacity", 1)
        .attr("transform", function(d) {
          return d.x < 180 ? "translate(0)" : "rotate(180)translate(-" + (d.name.length + 50) + ")";
        });

      // TODO: appropriate transform
      var nodeExit = node.exit().transition()
        .duration(duration)
        //.attr("transform", function(d) { return "diagonal(" + source.y + "," + source.x + ")"; })
        .remove();

      nodeExit.select("circle")
        .attr("r", 1e-6);

      nodeExit.select("text")
        .style("fill-opacity", 1e-6);

      // Update the links…
      var link = svg.selectAll("path.link")
        .data(links, function(d) {
          return d.target.id;
        });

      // Enter any new links at the parent's previous position.
      link.enter().insert("path", "g")
        .attr("class", "link")
        .attr("d", function(d) {
          var o = {
            x: source.x0,
            y: source.y0
          };
          return diagonal({
            source: o,
            target: o
          });
        });

      // Transition links to their new position.
      var arc = d3.svg.arc();

      link.transition()
        .duration(duration)
        .attr("d", function(d) {

          if (d.source.depth === 0) return "";
          
          if (d.source.children) {
            
            if (d.source.children[0] !== d.target &&
                d.source.children[d.source.children.length - 1] !== d.target)
              return ""
            
            var pad = 10,
              sum = 0;
            d.source.children.forEach(function(c) {
              sum += c.x;
            });

            // this is the mid point position
            var ma1 = ((sum / d.source.children.length) - 90) * (Math.PI / 180),
              ma2 = ((sum / d.source.children.length)) * (Math.PI / 180),
              mr = d.source.children[0].y - pad,
              mid = [mr * Math.cos(ma1), mr * Math.sin(ma1)];

            // this is the source position
            var sa = (d.source.x - 90) * (Math.PI / 180),
              sr = d.source.y - pad,
              source = [sr * Math.cos(sa), sr * Math.sin(sa)];

            // this is the final target position
            var ta = (d.target.x) * (Math.PI / 180),
              tr = d.target.y - pad,
              target = [tr * Math.cos(ta), tr * Math.sin(ta)];

            // this is the arc from mid to target
            var dx = target[0] - source[0],
              dy = target[1] - source[1],
              dr = Math.sqrt(dx * dx + dy * dy);
              
            arc.innerRadius(tr-1)
              .outerRadius(tr)
              .startAngle(ma2)
              .endAngle(ta);
              
            console.log(arc())
            
            return "M" + source + "L" + mid + arc();

           /* return "M" + source +
              "L" + mid +
              "A" + dr + "," + dr + " 0 0," + (ma < ta ? 1 : 0) + " " + target[0] + "," + target[1];
           */
          }
        });

      // Transition exiting nodes to the parent's new position.
      link.exit().transition()
        .duration(duration)
        .attr("d", function(d) {
          var o = {
            x: source.x,
            y: source.y
          };
          return diagonal({
            source: o,
            target: o
          });
        })
        .remove();

      // Stash the old positions for transition.
      nodes.forEach(function(d) {
        d.x0 = d.x;
        d.y0 = d.y;
      });
    }

    // Toggle children on click.
    function click(d) {
      if (d.children) {
        d._children = d.children;
        d.children = null;
      } else {
        d.children = d._children;
        d._children = null;
      }

      update(d);
    }

    // Collapse nodes
    function collapse(d) {
      if (d.children) {
        d._children = d.children;
        d._children.forEach(collapse);
        d.children = null;
      }
    }
  </script>
</body>

</html>

Plunker版本here