Javascript D3树布局-当子项超过一定数量时,自定义垂直布局

Javascript D3树布局-当子项超过一定数量时,自定义垂直布局,javascript,svg,tree,d3.js,data-visualization,Javascript,Svg,Tree,D3.js,Data Visualization,我正在尝试使用D3树布局来创建一个类别的族谱,我注意到的一件事是,当我有许多子节点时,它会在屏幕上水平延伸。理想情况下,我希望这些节点有一个更垂直的布局,这样人们就不必在屏幕上滚动,只需一直往下看 以下是我目前看到的情况: 现在可能没那么糟糕,但如果我说有20个孩子,它会横跨整个屏幕,这是我想要避免的事情 我见过这样的问题,但这对我没有帮助,因为我想要一个特定的布局,而不是简单的调整大小。。。我有很大的节点,如果我尝试动态调整树的大小,它们会开始相互碰撞——收缩树对我没有任何好处我特别需要一个

我正在尝试使用D3树布局来创建一个类别的族谱,我注意到的一件事是,当我有许多子节点时,它会在屏幕上水平延伸。理想情况下,我希望这些节点有一个更垂直的布局,这样人们就不必在屏幕上滚动,只需一直往下看

以下是我目前看到的情况:

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

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

这就是我所期望的。请注意,根目录没有使用这种格式,因为它只有4个子目录。理想情况下,我希望这样,如果一个家长有5个或更多的孩子,它会导致下面的布局。如果根目录有5个子目录,则会产生此布局,如果用户希望看到根目录的子目录(A、B、C…节点),则布局应简单地垂直延伸。如有必要,我可以得到一张运行图:

我发现了一个问题,他说他必须处理实际的d3js代码。我有点想避免这种情况,所以我希望看看d3js现在是否可以做到这一点,如果可以,怎么做?我不需要一个完整的答案,但是一段证明这是可能的代码将非常有用


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

您可以动态地计算子元素,并相应地调整DOM元素的宽度/高度。我建议计算任何级别中的最大子级数,可以使用d3.tree.nodes()提供的
depth
属性计算,也可以通过递归遍历树来计算。修改前一个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);    
看看这把小提琴:

我从中获取了示例代码并进行了一些修改。请注意,在该示例中,绘制时x和y会切换,因此布局显示为垂直树

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

原件:

固定的:

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


在两列布局中有子节点的节点看起来有点古怪,但更改线条绘制方法应该会有所改进。

仅从一眼看。。。我相当肯定这不会奏效。。。D3通过SVG绘制,SVG元素不会根据其周围DOM元素的宽度或高度改变其布局。它们被封装在一个SVG元素中,如果我收缩SVG元素,图形将被简单地切断。我需要一个实际更改节点布局和位置的解决方案。在代码中,DOM元素和
树都进行了调整
tree
是一个
d3.layout.tree()
,并且
tree.size
得到了适当的调整。嗯,我想我明白你的意思了。好吧,我来试一试,看看我能想出什么。我感谢你抽出时间来帮助我回答我的问题(顺便说一句:),所以我一直在玩弄它,不幸的是,这对我的处境没有帮助。就像我说的,我想改变节点的布局。我不只是想缩小它,使它适合。树确实变小了,而且适合得更好了,但是我特别想要一个不同的布局,用于我有更大节点的情况,并且我想要它们被隔开(在这些情况下,在它们开始相互碰撞之前,你只能将树缩小这么多)。。。我已经更新了我的问题。非常感谢你的例子。事实上,我昨天也意识到我可以如何改变x和y,所以我在玩这个。对于这种方法,我不能完全确定的一点是,即使我更改了节点的x和y,树仍然“思考”将布局视为分散的布局(如果设置了nodeSize,这一点很明显)。我更新了你的答案,并会在进一步测试后让你知道结果。是的,我做了一些修补,似乎手动更改x和y会严重破坏一切。我编写的函数以错误的方式遍历节点实际上,它应该是nodes.reverse().forEach,但这破坏了一切,因为它假定只扩展了一个分支。如果您对定位不满意,您可能需要重新实现Reingold-Tilford算法d3使用的多行支持,或者可能覆盖tree.separation方法,以欺骗d3,使其认为双倍行需要一半的间距。嗯,我没有考虑更改分离功能(在d3中完全允许)。我可能也会玩这个。非常感谢你的建议。我会在可能的时候再告诉你的。我已经做好了布局!现在我只需要修复分离,但这绝对是一个很棒的答案。非常感谢:)如果我知道如何修复节点之间的分离,我会编辑它并将其添加到您的答案中,但现在我会接受这一点,因为它肯定有正确的想法。实际上,似乎将空元素添加到数据中是一个坏主意。我在家里更新了小提琴。我所做的是创建两个数组并抛出垂直布局路径
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]);
// 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;
        });
    }
});