gpt4 book ai didi

svg - 在D3.js中打开子元素时,如何避免TreeMap上的文本元素重叠?

转载 作者:行者123 更新时间:2023-12-03 04:44:41 25 4
gpt4 key购买 nike

我在 D3.js 中基于 Mike Bostock 的节点链接树创建了一个树。我遇到的问题以及我在迈克的树中也看到的问题是,当没有足够的空间时,文本标签与圆形节点重叠/重叠,而不是扩展链接以留出一些空间。

作为新用户,我不允许上传图像,所以这里是 link转到迈克的树,您可以在其中看到前面节点的标签与后面节点重叠。

我尝试了各种方法来通过检测文本的像素长度来解决问题:

d3.select('.nodeText').node().getComputedTextLength();

但是,只有在渲染页面后,当我在渲染之前需要最长文本项的长度时,这才有效。

在渲染之前获取最长的文本项:

nodes = tree.nodes(root).reverse();

var longest = nodes.reduce(function (a, b) {
return a.label.length > b.label.length ? a : b;
});

node = vis.selectAll('g.node').data(nodes, function(d, i){
return d.id || (d.id = ++i);
});

nodes.forEach(function(d) {
d.y = (longest.label.length + 200);
});

仅返回字符串长度,而使用时

d.y = (d.depth * 200);

使每个链接具有静态长度,并且当新节点打开或关闭时不会调整大小。

有没有办法避免这种重叠?如果是这样,执行此操作并保持树的动态结构的最佳方法是什么?

我可以想出 3 种可能的解决方案,但并不是那么简单:

  1. 检测标签长度并在超出子节点的地方使用省略号。 (这会降低标签的可读性)
  2. 通过检测标签长度并告诉链接进行相应调整来动态缩放布局。 (这是最好的,但似乎真的很困难
  3. 缩放 svg 元素并在标签开始溢出时使用滚动条。 (不确定这是否可能,因为我一直假设 SVG 需要有一个设定的高度和宽度)。

最佳答案

所以下面的方法可以给不同层次的布局赋予不同的“高度”。您必须注意,使用径向布局时,您可能会面临没有足够的散布让小圆圈在不重叠的情况下散开文本的风险,但现在让我们忽略这一点。

关键是要认识到树布局只是将事物映射到宽度和高度的任意空间,并且对角线投影将宽度 (x) 映射到角度,将高度 (y) 映射到半径。此外,半径是树深度的简单函数。

所以here是一种根据文本长度重新分配深度的方法:

首先,我使用以下 (jQuery) 来计算最大文本大小:

var computeMaxTextSize = function(data, fontSize, fontName){
var maxH = 0, maxW = 0;

var div = document.createElement('div');
document.body.appendChild(div);
$(div).css({
position: 'absolute',
left: -1000,
top: -1000,
display: 'none',
margin:0,
padding:0
});

$(div).css("font", fontSize + 'px '+fontName);

data.forEach(function(d) {
$(div).html(d);
maxH = Math.max(maxH, $(div).outerHeight());
maxW = Math.max(maxW, $(div).outerWidth());
});

$(div).remove();
return {maxH: maxH, maxW: maxW};
}

现在我将递归地构建一个数组,其中每个级别都有一个字符串数组:

var allStrings = [[]];
var childStrings = function(level, n) {
var a = allStrings[level];
a.push(n.name);

if(n.children && n.children.length > 0) {
if(!allStrings[level+1]) {
allStrings[level+1] = [];
}
n.children.forEach(function(d) {
childStrings(level + 1, d);
});
}
};
childStrings(0, root);

然后计算每个级别的最大文本长度。

var maxLevelSizes = [];
allStrings.forEach(function(d, i) {
maxLevelSizes.push(computeMaxTextSize(allStrings[i], '10', 'sans-serif'));
});

然后我计算所有级别的总文本宽度(添加小圆圈图标的间距和一些填充以使其看起来不错)。这将是最终布局的半径。请注意,稍后我将再次使用相同的填充量。

var padding = 25; // Width of the blue circle plus some spacing
var totalRadius = d3.sum(maxLevelSizes, function(d) { return d.maxW + padding});

var diameter = totalRadius * 2; // was 960;

var tree = d3.layout.tree()
.size([360, totalRadius])
.separation(function(a, b) { return (a.parent == b.parent ? 1 : 2) / a.depth; });

现在我们可以像往常一样调用布局了。最后一点:要计算出不同级别的半径,我们需要前一级别半径的累积和。一旦我们有了,我们只需将新的半径分配给计算节点。

// Compute cummulative sums - these will be the ring radii
var newDepths = maxLevelSizes.reduce(function(prev, curr, index) {
prev.push(prev[index] + curr.maxW + padding);
return prev;
},[0]);

var nodes = tree.nodes(root);

// Assign new radius based on depth
nodes.forEach(function(d) {
d.y = newDepths[d.depth];
});

呃瞧!这可能不是最干净的解决方案,也可能不能解决所有问题,但它应该可以帮助您入门。玩得开心!

关于svg - 在D3.js中打开子元素时,如何避免TreeMap上的文本元素重叠?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12314749/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com