我在D3.js中根据Mike Bostock的Node-link Tree创建了一个Tree。我在Mike的树中看到的问题是,当没有足够的空间而不是扩展链接以留出一些空间时,文本标签会重叠/重叠圆形节点。
作为新用户,我不允许上传图片,因此这里是迈克树的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种可能的解决方案但不是那么简单:
答案 0 :(得分:8)
因此,以下方法可以给出不同级别的布局不同的“高度”。你必须注意,使用径向布局你可能没有足够的传播让小圆圈散开文本而没有重叠,但是现在让我们忽略它。
关键是要认识到树形布局只是将事物映射到宽度和高度的任意空间,并且对角线投影将宽度(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];
});
瞧瞧!这可能不是最干净的解决方案,也许并不能解决所有问题,但它应该让你开始。玩得开心!