5

我正在尝试使用 D3 树布局来创建各种家谱,我注意到的一件事是,当我有很多子节点时,它会在屏幕上水平伸展。理想情况下,我希望这些节点的布局更加垂直,这样人们就不必在屏幕上滚动,而可以继续向下看树。

这是我目前看到的:

在此处输入图像描述

现在可能没那么糟糕,但如果我说 20 个孩子,它会跨越整个屏幕,这是我想要避免的事情。

我见过这样的问题,但这对我没有帮助,因为我想要一个特定的布局,而不仅仅是调整大小......我有大节点,如果我尝试动态调整树的大小,它们就会开始相互碰撞——缩小这棵树对我没有任何好处。对于有超过一定数量的孩子的情况,我特别需要不同的布局。

这是我所设想/希望的。请注意,根不会生成这种格式,因为它只有 4 个子级。理想情况下,我想要这样,如果父母有 5 个或更多孩子,它会导致下面的布局。如果根有 5 个子节点,则将产生这种布局,如果用户想要查看根的孙子节点(A、B、C... 节点),布局应该简单地垂直拉伸。如有必要,我可以得到一张图表: 在此处输入图像描述

我发现了一个关于自定义子布局的半相似问题,他说他必须使用实际的 d3js 代码。我有点想避免这种情况,所以我希望找出现在的 d3js 是否可行,如果可以,该怎么做?我不需要完整的答案,但证明这是可能的代码片段会非常有帮助。

如有必要,我可以上传一个 JSFiddle 供人们玩耍。

4

2 回答 2

6

看看这个小提琴:

http://jsfiddle.net/dyXzu/

我从http://bl.ocks.org/mbostock/4339083获取示例代码并进行了一些修改。请注意,在示例中,绘制时切换 x 和 y,因此布局显示为垂直树。

我做的重要事情是修改深度计算器:

原来的:

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

固定的:

// Normalize for fixed-depth.
nodes.forEach(function (d) {
    d.y = d.depth * 180;
    if (d.parent != null) {
        d.x =  d.parent.x - (d.parent.children.length-1)*30/2
        + (d.parent.children.indexOf(d))*30;
    }
    // if the node has too many children, go in and fix their positions to two columns.
    if (d.children != null && d.children.length > 4) {
        d.children.forEach(function (d, i) {
            d.y = (d.depth * 180 + i % 2 * 100);
            d.x =  d.parent.x - (d.parent.children.length-1)*30/4
            + (d.parent.children.indexOf(d))*30/2 - i % 2 * 15;
        });
    }
});

基本上,我手动计算每个节点的位置,覆盖 d3 的默认节点定位。请注意,现在 x 没有自动缩放。您可能可以通过首先检查并计算打开的节点(如果存在,d.children 不为空,d._children 在节点关闭时存储节点),然后将总 x 相加来手动计算出这一点。

两列布局中带有子节点的节点看起来有点古怪,但改变画线方法应该会有所改善。

于 2013-08-09T16:25:46.677 回答
0

您可以动态计算子元素并相应地调整 DOM 元素的宽度/高度。我建议计算任何级别的最大子节点数,这可以使用depthd3.tree.nodes() 提供的属性来计算,或者只是通过递归地沿着树向下计算。修改之前 SO 答案中的一些代码,您可能会遇到这样的情况:

    var levelWidth = [1];
    var childCount = function (level, n) {
            if (n.children && n.children.length > 0) {
            if (levelWidth.length <= level + 1) levelWidth.push(0);
                 levelWidth[level + 1] += n.children.length;
            n.children.forEach(function (d) {
                childCount(level + 1, d);
            });
        }
    };
    childCount(0, root);
    var newHeight = d3.max(levelWidth) * 40;  
    tree = tree.size([Math.max(newHeight, 660), w]);
        document.getElementById("#divID").setAttribute("height", Math.max(newHeight, 660) + 50);    

或者使用nodes()方法。

var nodes = tree.nodes(root), arr = [];
for(i = 0; i < nodes.length; i++)
    arr[nodes[i].depth]++;
var max = 0;
for(i = 0; i <nodes.length; i++)
    if(arr[i] > max)
        max = arr[i];
$('#elementOfChoice').css("height", max*40);
var newHeight = max * 40;  
tree = tree.size([Math.max(newHeight, 660), w]);
于 2013-08-07T17:22:51.320 回答