指定nodeSize时d3树的居中更改

指定nodeSize时d3树的居中更改

本文介绍了指定nodeSize时d3树的居中更改的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为此使用d3.v4.当我有

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

树正好居中

但是(因为我想指定节点之间的垂直间距),当我将其更改为

var treemap = d3.tree().size([height, width]).nodeSize([40]);

树移到左上方:

有人知道为什么会这样吗?

我相信d3.tree().size()d3.tree().nodeSize()是互斥的.设置一个会使另一个无效. v3文档(此处)在这方面是明确的,而v4文档(这里)可能暗示这仍然是正确的.快速测试表明,v4在这方面与v4相同. (另请参见:此答案,或这一个.)

更重要的是,指定节点大小时,根节点始终位于⟨0,0⟩". ( API文档).

使用size时,每个节点的定位都利用了可用空间-相对于指定的宽度/高度进行定位,而无需考虑节点密度.使用nodeSize时,位置是相对于其他节点的,而不是该图的可用区域(尽管它将锚定在[0,0]).通过最初使用.size,您不必担心定位.使用.nodeSize,您必须为节点设置一个转换,以使它们正确居中.

要正确放置它,您需要在容器上设置一个转换,以便更适当地放置[0,0]和根节点(如下面的无边距示例中的[0,高度/2]):

 var data = { "name": "Parent", "children": [ { "name": "Child A", "children": [ { "name": "Grandchild" } ] }, { "name": "Child B", } ] };

var width = 500;
var height = 500;
var svg = d3.select("body").append("svg")
      .attr("width", width)
      .attr("height", height);

var g = svg.append("g").attr('transform','translate(0,'+ (height/2) +')');
var root = d3.hierarchy(data);

var tree = d3.tree()
    .nodeSize([width/2,height/2]);

 var link = g.selectAll(".link")
    .data(tree(root).links())
    .enter().append("path")
      .attr("class", "link")
      .attr("d", d3.linkHorizontal()
          .x(function(d) { return d.y; })
          .y(function(d) { return d.x; }));

  var node = g.selectAll(".node")
    .data(root.descendants())
    .enter().append("g")
      .attr("class", function(d) { return "node" + (d.children ? " node--internal" : " node--leaf"); })
      .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })

  node.append("circle")
      .attr("r", 2.5); 
 .node circle {
  fill: #fff;
  stroke: steelblue;
  stroke-width: 3px;
}

.link {
  fill: none;
  stroke: #ccc;
  stroke-width: 2px;
}
svg {
	background: #eee;
} 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min.js"></script> 

d3.tree().size()的自动间距相比:

 var data = { "name": "Parent", "children": [ { "name": "Child A", "children": [ { "name": "Grandchild" } ] }, { "name": "Child B", } ] };

var width = 500;
var height = 500;
var svg = d3.select("body").append("svg")
      .attr("width", width)
      .attr("height", height);

var g = svg.append("g");
var root = d3.hierarchy(data);

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

 var link = g.selectAll(".link")
    .data(tree(root).links())
    .enter().append("path")
      .attr("class", "link")
      .attr("d", d3.linkHorizontal()
          .x(function(d) { return d.y; })
          .y(function(d) { return d.x; }));

  var node = g.selectAll(".node")
    .data(root.descendants())
    .enter().append("g")
      .attr("class", function(d) { return "node" + (d.children ? " node--internal" : " node--leaf"); })
      .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })

  node.append("circle")
      .attr("r", 2.5); 
 .node circle {
  fill: #fff;
  stroke: steelblue;
  stroke-width: 3px;
}

.link {
  fill: none;
  stroke: #ccc;
  stroke-width: 2px;
}
svg {
	background: #eee;
} 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min.js"></script> 

I'm using d3.v4 for this. When I have

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

the tree is nicely centred

but (because I want to specify the vertical spacing between nodes) when I change this to

var treemap = d3.tree().size([height, width]).nodeSize([40]);

the tree moves to the top left:

Does anyone know why this might be happening?

解决方案

I believe that d3.tree().size() and d3.tree().nodeSize() are exclusive in relation to one another. Setting one nullifies the other. The v3 documentation (here) is explicit in this regard , while the v4 documentation (here) might suggest that this is still true. Quick testing suggests v4 is the same as v4 in regards to this. (see also: this answer, or this one.)

More importantly, "When a node size is specified, the root node is always positioned at ⟨0, 0⟩." (API documentation).

When using size, the positioning of each node takes advantage of available space - positioning in relation to the specified width/height with no consideration for node density. When using nodeSize, positioning is relative to other nodes, not the available area for the diagram (though it will be anchored at [0,0]). By initially using .size, you didn't have to worry about positioning. Using .nodeSize, you'll have to set a transform for your nodes so that they are centered properly.

To position it properly, you'll need to set a transform on your container so that [0,0], and the root node, is more appropriately placed (as in the margin-less example below, at [0,height/2]):

var data = { "name": "Parent", "children": [ { "name": "Child A", "children": [ { "name": "Grandchild" } ] }, { "name": "Child B", } ] };

var width = 500;
var height = 500;
var svg = d3.select("body").append("svg")
      .attr("width", width)
      .attr("height", height);

var g = svg.append("g").attr('transform','translate(0,'+ (height/2) +')');
var root = d3.hierarchy(data);

var tree = d3.tree()
    .nodeSize([width/2,height/2]);

 var link = g.selectAll(".link")
    .data(tree(root).links())
    .enter().append("path")
      .attr("class", "link")
      .attr("d", d3.linkHorizontal()
          .x(function(d) { return d.y; })
          .y(function(d) { return d.x; }));

  var node = g.selectAll(".node")
    .data(root.descendants())
    .enter().append("g")
      .attr("class", function(d) { return "node" + (d.children ? " node--internal" : " node--leaf"); })
      .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })

  node.append("circle")
      .attr("r", 2.5);
.node circle {
  fill: #fff;
  stroke: steelblue;
  stroke-width: 3px;
}

.link {
  fill: none;
  stroke: #ccc;
  stroke-width: 2px;
}
svg {
	background: #eee;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min.js"></script>

As compared with the automatic spacing of d3.tree().size():

var data = { "name": "Parent", "children": [ { "name": "Child A", "children": [ { "name": "Grandchild" } ] }, { "name": "Child B", } ] };

var width = 500;
var height = 500;
var svg = d3.select("body").append("svg")
      .attr("width", width)
      .attr("height", height);

var g = svg.append("g");
var root = d3.hierarchy(data);

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

 var link = g.selectAll(".link")
    .data(tree(root).links())
    .enter().append("path")
      .attr("class", "link")
      .attr("d", d3.linkHorizontal()
          .x(function(d) { return d.y; })
          .y(function(d) { return d.x; }));

  var node = g.selectAll(".node")
    .data(root.descendants())
    .enter().append("g")
      .attr("class", function(d) { return "node" + (d.children ? " node--internal" : " node--leaf"); })
      .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })

  node.append("circle")
      .attr("r", 2.5);
.node circle {
  fill: #fff;
  stroke: steelblue;
  stroke-width: 3px;
}

.link {
  fill: none;
  stroke: #ccc;
  stroke-width: 2px;
}
svg {
	background: #eee;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min.js"></script>

这篇关于指定nodeSize时d3树的居中更改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-21 05:37