D3树形布局 - 当儿童超过一定数量时自定义垂直布局

时间:2013-08-07 16:39:19

标签: javascript svg tree d3.js data-visualization

我正在尝试使用D3 Tree Layout来创建各种各样的系列树,其中一件事我注意到,当我有很多子节点时,它会在屏幕上水平伸展。理想情况下,我希望这些节点有一个更垂直的布局,这样人们就不必在屏幕上滚动,只能一直向下看树。

以下是我目前看到的内容:

enter image description here

现在它可能不是那么糟糕,但如果我说了20个孩子,它将跨越整个屏幕,这是我有点想避免的。

我看过像this这样的问题,但这对我没有帮助,因为我想要一个特定的布局,而不仅仅是调整大小...我有大节点,如果我试图它们就会开始相互碰撞动态调整树的大小 - 缩小树对我没有任何好处。 我特别需要针对超过一定数量儿童的情况采用不同的布局。

这是我想象/希望的那种。请注意,root不会生成此格式,因为它只有4个子节点。理想情况下,我想要它,如果父母有5个或更多的孩子,它将导致下面的布局。如果根有5个子节点,则会导致这种布局,如果用户想要查看根的孙子节点(A,B,C ......节点),布局应该只是垂直伸展。如果有必要,我可以得到一个图表: enter image description here

我找到了semi-similar question regarding custom children layouts,他说他必须使用实际的d3js代码。我有点想避免这种情况,所以我希望能够找到d3js是否可行,因为它现在就是这样,如果是这样,怎么去呢?我不需要一个完整的答案,但是一段代码证明这是可能的,这将非常有帮助。

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

2 个答案:

答案 0 :(得分:4)

看看这个小提琴:

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相加。

具有两列布局中的子项的节点看起来有点时髦,但更改线条绘制方法应该可以改进。

答案 1 :(得分:0)

您可以动态计算子项并相应地调整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);    

或使用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]);