y3.layout.tree中链接的y坐标

时间:2015-01-15 07:40:42

标签: svg d3.js

我的问题最好通过问题中进一步包含的图像来解释。希望你们中的一位D3专家(比如Lars)发现这个问题并帮助我。在此先感谢,非常感谢任何帮助!

我正在构建http://bl.ocks.org/d3noob/8375092,扩展了包含深度和元数据的示例数据集,并使用该元数据来确定节点链接的笔触宽度(" amount"属性)以及圆的半径(" avg_dwell"属性)。

到目前为止,D3非常棒,而且layout.tree组件可以创造奇迹。但是,我已经走到了尽头。我需要实现这两张图片中显示的内容:http://imgur.com/a/0aHWq#37MvNa7

因此,将这些路径堆叠在一起意味着使用节点' "量"属性为高度,循环遍历并逐个确定它们的y坐标。

我的问题实际上是我不知道在代码中该怎么做。我的代码如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">

    <title>Collapsible Tree Example</title>

    <style>

 .node circle {
   fill: #fff;
   stroke: steelblue;
   stroke-width: 3px;
 }

 .node text { font: 12px sans-serif; }

 .link {
   fill: none;
   /*stroke: #ccd;
   stroke-width: 5px;*/
 }

    </style>

  </head>

  <body>
<script src="http://d3js.org/d3.v3.min.js"></script>

<script>

var treeData = [
  {
    "name": "Root",
    "parent": null,
    "amount": 10000,
    "maxAmount": 3500,
    "maxDwell": 19,
    "avg_dwell": 2,
    "children": [
      {
        "name": "Zone 1",
        "amount": 500,
        "avg_dwell": 2,
        "children": [
          {
            "name": "Zone 3",
            "amount": 400,
            "avg_dwell": 8,
            "children": [
              {
                "name": "Zone 4",
                "amount": 250,
                "avg_dwell": 19
              },
              {
                "name": "Zone 7",
                "amount": 130,
                "avg_dwell": 5

              }
            ]
          },
          {
            "name": "Zone 5",
            "amount": 45,
            "avg_dwell": 4

          }
        ]
      },
      {
        "name": "Zone 4",
        "amount": 1200,
        "avg_dwell": 4,
         "children": [
          {
            "name": "Zone 7",
            "amount": 1000,
            "avg_dwell": 4,
            "children": [
              {
                "name": "Zone 5",
                "amount": 800,
                "avg_dwell": 4
              },
              {
                "name": "Zone 6",
                "amount": 200,
                "avg_dwell": 10
              }
            ]
          },
          {
            "name": "Zone 2",
            "amount": 200,
            "avg_dwell": 4

          }
        ]
      },
      {
        "name": "Zone 8",
        "amount": 2500,
        "avg_dwell": 4,
        "children": [
          {
            "name": "Zone 6",
            "amount": 1000,
            "avg_dwell": 15,
            "children": [
              {
                "name": "Zone 1",
                "amount": 350,
                "avg_dwell": 18
              },
              {
                "name": "Zone 2",
                "amount": 650,
                "avg_dwell": 15

              }
            ]
          },
          {
            "name": "Zone 3",
            "amount": 1500,
            "avg_dwell": 4

          }
        ]

      },
      {
        "name": "Zone 6",
        "amount": 800,
        "avg_dwell": 15
      }
      ,
      {
        "name": "Zone 7",
        "amount": 1000,
        "avg_dwell": 8
      }
      ,
      {
        "name": "Zone 3",
        "amount": 3500,
        "avg_dwell": 9,
        "children": [
          {
            "name": "Zone 4",
            "amount": 1000,
            "avg_dwell": 15,
            "children": [
              {
                "name": "Zone 1",
                "amount": 350,
                "avg_dwell": 18
              },
              {
                "name": "Zone 2",
                "amount": 650,
                "avg_dwell": 15

              }
            ]
          },
          {
            "name": "Zone 9",
            "amount": 1500,
            "avg_dwell": 4,
            "children": [
              {
                "name": "Zone 8",
                "amount": 1000,
                "avg_dwell": 18
              },
              {
                "name": "Zone 5",
                "amount": 500,
                "avg_dwell": 15

              }
            ]

          }
        ]
      }

    ]
  }
];

// tooltip div

var div = d3.select("body").append("div")   
    .attr("class", "tooltip")               
    .style("opacity", 0);

// ************** Generate the tree diagram  *****************
var margin = {top: 20, right: 120, bottom: 20, left: 120},
  width = 960 - margin.right - margin.left,
  height = 500 - margin.top - margin.bottom;

var radiusDivider = treeData[0].maxDwell/20; // for circle size
var pathThicknessDivider = treeData[0].maxAmount/30; // relative thickness of path connectors
var textDistanceQuotient = 1.0 // how far is the text from the nodes?

var allValues = d3.values(treeData); 

var i = 0,
  duration = 750,
  root;

var tree = d3.layout.tree()
  .size([height, width]);

var diagonal = d3.svg.diagonal()
                      .projection(function(d) { 
                        return [d.y, d.x]; 
                      });

var svg = d3.select("body").append("svg")
  .attr("width", width + margin.right + margin.left)
  .attr("height", height + margin.top + margin.bottom)
  .append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

root = treeData[0];
root.x0 = height / 2;
root.y0 = 0;

update(root);

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

function update(source) {

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

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

  // 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 "translate(" + source.y0 + "," + source.x0 + ")"; })
    .on("click", click);

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

  nodeEnter.append("text")
    .attr("x", function(d) { 
      return d.children || d._children ? (d.avg_dwell * textDistanceQuotient) * -1 : d.avg_dwell * textDistanceQuotient; 
    })
    .attr("dy", ".35em")
    .attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
    .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 "translate(" + d.y + "," + d.x + ")"; });

  nodeUpdate.select("circle")
    .attr("r", function(d){
      return d.avg_dwell/radiusDivider; // circle size depends on avg_dwell
      //return 1e-6;
    })
    .style("fill", function(d) { 
      return d._children ? "lightsteelblue" : "#fff"; 
    })
    .style("fill-opacity", 1);

  nodeUpdate.select("text")
    .style("fill-opacity", 1);

  // Transition exiting nodes to the parent's new position.
  var nodeExit = node.exit().transition()
    .duration(duration)
    .attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
    .remove();

  nodeExit.select("circle")
    .attr("r", function(d){
      //return d.amount/radiusDivider; // circle size depends on avg_dwell
      return 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};
      console.log(o);
      return diagonal({source: o, target: o});
    })
    .attr("id", function(d, i){
      return i;
    })
    .style("stroke", "blue")
    .style("stroke-opacity", 0.5)
    .style("stroke-width", function(d){
      return d.target.amount / pathThicknessDivider;
      //return Math.ceil(d.amount/80);
    })
    .on("click", click);



  // Transition links to their new position.
  link.transition()
    .duration(duration)
    .attr("d", diagonal);

  // 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};
      //console.log(o);
      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 and do other stuff 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);
}

</script>

  </body>
</html>

我知道每个路径的原始坐标都是在这部分中生成的:

// 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};
          console.log(o);
          return diagonal({source: o, target: o});
        })
        .attr("id", function(d, i){
          return i;
        })
        .style("stroke", "blue")
        .style("stroke-opacity", 0.5)
        .style("stroke-width", function(d){
          return d.target.amount / pathThicknessDivider;
          //return Math.ceil(d.amount/80);
        })
        .on("click", click);

0 个答案:

没有答案