特定深度的 d3 集群布局节点未对齐

d3 cluster layout nodes at a particular depth are not aligned

我有以下 JSFiddle 代码:

http://jsfiddle.net/s4Mjx/27/

我希望 node2node3 垂直对齐,因为它们处于相同的深度。

我还希望所有叶节点也对齐。

但是,此代码仅对齐叶节点,其余节点处于不同级别。

最简单的是使用树布局,它将节点对齐depth,然后将叶节点移动到最大深度的位置。

var root = getData();
var nodes = tree.nodes(root);
var links = tree.links(nodes);
var maxY = d3.max(nodes, d => d.y);
var linkSources = new Set(links.map(d => d.source.name));
nodes.forEach( d => { if (!linkSources.has(d.name)) { d.y = maxY; } });

一个完整的例子:

var width = 500,
    height = 500;
var diameter = 300;
var duration = 2000;

var tree = d3.layout.tree()
    .size([height, width - 160]);

var diagonal = d3.svg.diagonal()
    .projection(function (d) { return [d.y, d.x]; });

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height)
    .append("g")
    .attr("transform", "translate(80,0)");

    var root = getData();
    var nodes = tree.nodes(root);
    var links = tree.links(nodes);
    var maxY = d3.max(nodes, d => d.y);
    var linkSources = new Set(links.map(d => d.source.name));
    nodes.forEach( d => { if (!linkSources.has(d.name)) { d.y = maxY; } });

    var link = svg.selectAll(".link")
        .data(links)
       .enter()
        .append("path")
        .attr("class", "link")
        .style("stroke", "#8da0cb")
        .attr("d", diagonal);

    var node = svg.selectAll(".node")
        .data(nodes)
       .enter()
        .append("g")
        .attr("class", "node")
        .attr("transform", function (d) {
        return "translate(" + d.y + "," + d.x + ")";
    })

    node.append("circle")
        .attr("r", 4.5);

    node.append("text")
        .attr("dx", function (d) { return d.children ? -8 : 8; })
        .attr("dy", 3)
        .style("text-anchor", function (d) { return d.children ? "end" : "start"; })
        .text(function (d) { return d.name; });

function getData() {
    return {
       name: "node1",
       id: "1",
       children: [
         {
            name: "node2",
            id: "2",
            children: [
             {
               name: "node5",
              id: "5",
                children: [
                {
                  name: "node6",
                  id: "6",
                   children: [
                {
                  name: "node7",
                  id: "7",
                  
                }
            ]
                }
            ]
              }
            ]
         },
         {
            name: "node3",
            id: "3",
             children: [
                {
                  name: "node8",
                  id: "8",
                }
            ]
         },
         {
            name: "node4",
            id: "4",
         }
       ]
    };
}
.node circle {
    fill: #fff;
    stroke: steelblue;
    stroke-width: 1.5px;
}
.node {
    font: 10px sans-serif;
}
.link {
    fill: none;
    stroke: tan;
    stroke-width: 1.5px;
}
<script type="text/javascript" src="https://d3js.org/d3.v3.js"></script>

现在出现了一个不同的问题。对路径进行排序。这是一个完全不同的球类游戏,因为您必须根据直到叶节点的深度数递归进行。


也可以通过集群布局来完成。

找到每个深度的最小y,如果是源则调整节点的位置

var root = getData(),
    nodes = cluster.nodes(root),
    links = cluster.links(nodes);
var depthMinY = {};
nodes.forEach( d => { if (!depthMinY[d.depth]) { depthMinY[d.depth] = d.y;}
                      depthMinY[d.depth] = Math.min(depthMinY[d.depth], d.y);});
var linkSources = new Set(links.map(d => d.source.name));
nodes.forEach( d => { if (linkSources.has(d.name)) { d.y = depthMinY[d.depth]; } });

一个完整的例子:

var width = 500,
    height = 500;
var diameter = 300;
var duration = 2000;

var cluster = d3.layout.cluster()
    .size([height, width - 160]);

var diagonal = d3.svg.diagonal()
    .projection(function (d) {
    return [d.y, d.x];
});

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height)
    .append("g")
    .attr("transform", "translate(80,0)");

    var root = getData(),
        nodes = cluster.nodes(root),
        links = cluster.links(nodes);
    var depthMinY = {};
    nodes.forEach( d => { if (!depthMinY[d.depth]) { depthMinY[d.depth] = d.y;}
                          depthMinY[d.depth] = Math.min(depthMinY[d.depth], d.y);});
    var linkSources = new Set(links.map(d => d.source.name));
    nodes.forEach( d => { if (linkSources.has(d.name)) { d.y = depthMinY[d.depth]; } });
    
    var link = svg.selectAll(".link")
        .data(links)
       .enter()
        .append("path")
        .attr("class", "link")
        .style("stroke", "#8da0cb")
        .attr("d", diagonal);

    var node = svg.selectAll(".node")
        .data(nodes)
       .enter()
        .append("g")
        .attr("class", "node")
        .attr("transform", function (d) {
        return "translate(" + d.y + "," + d.x + ")";
    })

    node.append("circle")
        .attr("r", 4.5);

    node.append("text")
        .attr("dx", function (d) { return d.children ? -8 : 8; })
        .attr("dy", 3)
        .style("text-anchor", function (d) { return d.children ? "end" : "start"; })
        .text(function (d) { return d.name; });


function getData() {
    return {
       name: "node1",
       id: "1",
       children: [
         {
            name: "node2",
            id: "2",
            children: [
             {
               name: "node5",
              id: "5",
                children: [
                {
                  name: "node6",
                  id: "6",
                   children: [
                {
                  name: "node7",
                  id: "7",
                  
                }
            ]
                }
            ]
              }
            ]
         },
         {
            name: "node3",
            id: "3",
             children: [
                {
                  name: "node8",
                  id: "8",
             children: [
                {
                  name: "node9",
                  id: "9",
                }
                 ] 
                }
            ]
         },
         {
            name: "node4",
            id: "4",
         }
       ]
    };
}
.node circle {
    fill: #fff;
    stroke: steelblue;
    stroke-width: 1.5px;
}
.node {
    font: 10px sans-serif;
}
.link {
    fill: none;
    stroke: tan;
    stroke-width: 1.5px;
}
<script type="text/javascript" src="https://d3js.org/d3.v3.js"></script>