d3树布局可视化-继承具有多个父级的子级

tyu7yeag  于 2021-09-29  发布在  Java
关注(0)|答案(2)|浏览(331)

我是d3可视化的新鲜蜜蜂。目前正在为数据沿袭创建d3树布局可视化。在数据沿袭流中,子节点可以从多个父节点派生。这是一个例子。在下面的示例中,一个“devlead”可以与两个经理一起工作。

var data = [
     { "name": "Director", "parent": "null", "depth": 0 },
     { "name": "Manager1", "parent": "Director", "depth": 1 },
     { "name": "Manager2", "parent": "Director", "depth": 1 },
     { "name": "DevLead", "parent": "Manager1", "depth": 2 },
     { "name": "DevLead", "parent": "Manager2", "depth": 2 }
        ];

正在获取输出,请参阅下面的图片附件。

我想看到“devlead”子级应该只显示一个,并且应该有“manager1”和“manager2”的派生。有谁能帮上忙吗。

gg0vcinb

gg0vcinb1#

d3树布局不完全支持多个父级
你能做什么?
使用网络图代替-缺点是节点定位困难
我有类似的要求,并尝试建立网络图类似的树布局,但当有许多节点,它变得混乱。。。
你可以在codepen上查看
使用hack on tree布局-从其他节点绘制附加链接
检查这个例子
另一个使用隐藏节点的黑客——JSFIDLE
此外,我认为,这些链接将进一步帮助您:
d3.js中的家族树
d树库-具有多个父级的数据
如果使用第一个选项,您可以通过删除和添加数据中的节点来处理此代码段

<!DOCTYPE html>
<html >

<head>
  <meta charset="UTF-8">
  <link rel="shortcut icon" type="image/x-icon" href="https://production-assets.codepen.io/assets/favicon/favicon-8ea04875e70c4b0bb41da869e81236e54394d63638a1ef12fa558a4a835f1164.ico" />
  <link rel="mask-icon" type="" href="https://production-assets.codepen.io/assets/favicon/logo-pin-f2d2b6d2c61838f7e76325261b7195c27224080bc099486ddd6dccb469b8e8e6.svg" color="#111" />
  <title>CodePen - A Pen by  dato</title>

</head>

<body translate="no" >

  <script src='https://d3js.org/d3.v3.min.js'></script>

    <script>
    var width = window.innerWidth - 20,
  height = window.innerHeight - 20,
  radius = 30;

var min_zoom = 0.1;
var max_zoom = 7;

var zoom = d3.behavior.zoom().scaleExtent([min_zoom, max_zoom])

var fill = d3.scale.category20();

var force = d3.layout.force()

.charge(-8000)
  .linkDistance(200)

.size([width, height]);

force.drag().on("dragstart", dragstarted)

var svg = d3.select("body").append("svg")
  .attr("width", width)
  .attr("height", height);

var chart = svg.append('g');

var json = {
  "nodes": [{
    "name": "node0"
  }, {
    "name": "node1"
  }, {
    "name": "node2"
  }, {
    "name": "node3"
  }, {
    "name": "node4"
  }, {
    "name": "node5"
  }, {
    "name": "node6"
  }, {
    "name": "node7"
  }, {
    "name": "node8"
  }, {
    "name": "node9"
  }, {
    "name": "node10"
  }, {
    "name": "node11"
  }, {
    "name": "node12"
  }, {
    "name": "node13"
  }, {
    "name": "node14"
  }, {
    "name": "node15"
  }, {
    "name": "node16"
  }, {
    "name": "node17"
  }, {
    "name": "node18"
  }, {
    "name": "node19"
  }, {
    "name": "node20"
  }, {
    "name": "node21"
  }, {
    "name": "node22"
  }, {
    "name": "node23"
  }, {
    "name": "node24"
  }, {
    "name": "node25"
  }, {
    "name": "node26"
  }, {
    "name": "node27"
  }, {
    "name": "node28"
  }, {
    "name": "node29"
  }, {
    "name": "node30"
  }, {
    "name": "node31"
  }, {
    "name": "node32"
  }, {
    "name": "node33"
  }, {
    "name": "node34"
  }, {
    "name": "node35"
  }, {
    "name": "node36"
  }, {
    "name": "node37"
  }, {
    "name": "node38"
  }, {
    "name": "node39"
  }, {
    "name": "node40"
  }, {
    "name": "node41"
  }, {
    "name": "node42"
  }, {
    "name": "node43"
  }, {
    "name": "node44"
  }, {
    "name": "node45"
  }, {
    "name": "node46"
  }, {
    "name": "node47"
  }, {
    "name": "node48"
  }, {
    "name": "node49"
  }, {
    "name": "node50"
  }, {
    "name": "node51"
  }, {
    "name": "node52"
  }, {
    "name": "node53"
  }, {
    "name": "node54"
  }, {
    "name": "node55"
  }, {
    "name": "node56"
  }, {
    "name": "node57"
  }, {
    "name": "node58"
  }, {
    "name": "node59"
  }, {
    "name": "node60"
  }, {
    "name": "node61"
  }, {
    "name": "node62"
  }, {
    "name": "node63"
  }, {
    "name": "node64"
  }, {
    "name": "node65"
  }, {
    "name": "node66"
  }, {
    "name": "node67"
  }, {
    "name": "node68"
  }, {
    "name": "node69"
  }, {
    "name": "node70"
  }, {
    "name": "node71"
  }, {
    "name": "node72"
  }, {
    "name": "node73"
  }, {
    "name": "node74"
  }, {
    "name": "node75"
  }, {
    "name": "node76"
  }, {
    "name": "node77"
  }, {
    "name": "node78"
  }, {
    "name": "node79"
  }, {
    "name": "node80"
  }, {
    "name": "node81"
  }, {
    "name": "node82"
  }, {
    "name": "node83"
  }, {
    "name": "node84"
  }, {
    "name": "node85"
  }, {
    "name": "node86"
  }, {
    "name": "node87"
  }, {
    "name": "node88"
  }, {
    "name": "node89"
  }, {
    "name": "node90"
  }, {
    "name": "node91"
  }, {
    "name": "node92"
  }, {
    "name": "node93"
  }, {
    "name": "node94"
  }, {
    "name": "node95"
  }, {
    "name": "node96"
  }, {
    "name": "node97"
  }, {
    "name": "node98"
  }, {
    "name": "node99"
  }],
  "links": [ {
    "source": 0,
    "target": 1
  }, {
    "source": 0,
    "target": 2
  }, {
    "source": 1,
    "target": 3
  }, {
    "source": 1,
    "target": 4
  }, {
    "source": 2,
    "target": 5
  }, {
    "source": 2,
    "target": 6
  }, {
    "source": 3,
    "target": 7
  }, {
    "source": 3,
    "target": 8
  }, {
    "source": 4,
    "target": 9
  }, {
    "source": 4,
    "target": 10
  }, {
    "source": 5,
    "target": 11
  }, {
    "source": 5,
    "target": 12
  }, {
    "source": 6,
    "target": 13
  }, {
    "source": 6,
    "target": 14
  }, {
    "source": 7,
    "target": 15
  }, {
    "source": 7,
    "target": 16
  }, {
    "source": 8,
    "target": 17
  }, {
    "source": 8,
    "target": 18
  }, {
    "source": 9,
    "target": 19
  }, {
    "source": 9,
    "target": 20
  }, {
    "source": 10,
    "target": 21
  }, {
    "source": 10,
    "target": 22
  }, {
    "source": 11,
    "target": 23
  }, {
    "source": 11,
    "target": 24
  }, {
    "source": 12,
    "target": 25
  }, {
    "source": 12,
    "target": 26
  }, {
    "source": 13,
    "target": 27
  }, {
    "source": 13,
    "target": 28
  }, {
    "source": 14,
    "target": 29
  }, {
    "source": 14,
    "target": 30
  }, {
    "source": 15,
    "target": 31
  }, {
    "source": 15,
    "target": 32
  }, {
    "source": 16,
    "target": 33
  }, {
    "source": 16,
    "target": 34
  }, {
    "source": 17,
    "target": 35
  }, {
    "source": 17,
    "target": 36
  }, {
    "source": 18,
    "target": 37
  }, {
    "source": 18,
    "target": 38
  }, {
    "source": 19,
    "target": 39
  }, {
    "source": 19,
    "target": 40
  }, {
    "source": 20,
    "target": 41
  }, {
    "source": 20,
    "target": 42
  }, {
    "source": 21,
    "target": 43
  }, {
    "source": 21,
    "target": 44
  }, {
    "source": 22,
    "target": 45
  }, {
    "source": 22,
    "target": 46
  }, {
    "source": 23,
    "target": 47
  }, {
    "source": 23,
    "target": 48
  }, {
    "source": 24,
    "target": 49
  }, {
    "source": 24,
    "target": 50
  }, {
    "source": 25,
    "target": 51
  }, {
    "source": 25,
    "target": 52
  }, {
    "source": 26,
    "target": 53
  }, {
    "source": 26,
    "target": 54
  }, {
    "source": 27,
    "target": 55
  }, {
    "source": 27,
    "target": 56
  }, {
    "source": 28,
    "target": 57
  }, {
    "source": 28,
    "target": 58
  }, {
    "source": 29,
    "target": 59
  }, {
    "source": 29,
    "target": 60
  }, {
    "source": 30,
    "target": 61
  }, {
    "source": 30,
    "target": 62
  }, {
    "source": 31,
    "target": 63
  }, {
    "source": 31,
    "target": 64
  }, {
    "source": 32,
    "target": 65
  }, {
    "source": 32,
    "target": 66
  }, {
    "source": 33,
    "target": 67
  }, {
    "source": 33,
    "target": 68
  }, {
    "source": 34,
    "target": 69
  }, {
    "source": 34,
    "target": 70
  }, {
    "source": 35,
    "target": 71
  }, {
    "source": 35,
    "target": 72
  }, {
    "source": 36,
    "target": 73
  }, {
    "source": 36,
    "target": 74
  }, {
    "source": 37,
    "target": 75
  }, {
    "source": 37,
    "target": 76
  }, {
    "source": 38,
    "target": 77
  }, {
    "source": 38,
    "target": 78
  }, {
    "source": 39,
    "target": 79
  }, {
    "source": 39,
    "target": 80
  }, {
    "source": 40,
    "target": 81
  }, {
    "source": 40,
    "target": 82
  }, {
    "source": 41,
    "target": 83
  }, {
    "source": 41,
    "target": 84
  }, {
    "source": 42,
    "target": 85
  }, {
    "source": 42,
    "target": 86
  }, {
    "source": 43,
    "target": 87
  }, {
    "source": 43,
    "target": 88
  }, {
    "source": 44,
    "target": 89
  }, {
    "source": 44,
    "target": 90
  }, {
    "source": 45,
    "target": 91
  }, {
    "source": 45,
    "target": 92
  }, {
    "source": 46,
    "target": 93
  }, {
    "source": 46,
    "target": 94
  }, {
    "source": 47,
    "target": 95
  }, {
    "source": 47,
    "target": 96
  }, {
    "source": 48,
    "target": 97
  }, {
    "source": 48,
    "target": 98
  }, {
    "source": 49,
    "target": 99
  },{
    "source": 0,
    "target": 99
  }]
}

var link = chart.selectAll("line")
  .data(json.links)
  .enter()
  .append("line")
  .attr("stroke", function(d) {
    return 'blue'
  })

var node = chart.selectAll("circle")
  .data(json.nodes)
  .enter().append("circle")
  .attr("r", radius - .75)
  .style("fill", function(d) {
    return fill(d.group);
  })
  .style("stroke", function(d) {
    return d3.rgb(fill(d.group)).darker();
  })
  .on('mouseover', d => console.log(d))

.call(force.drag);

function dragstarted() {
  d3.event.sourceEvent.stopPropagation();
}

zoom.on("zoom", function(d) {

  var evt = d3.event;
  debugger;
  /*
	var dcx = (window.innerWidth/2-d.x*zoom.scale());
	var dcy = (window.innerHeight/2-d.y*zoom.scale());
  */
  var dcx = evt.translate[0]
  var dcy = evt.translate[1]

  zoom.translate([dcx, dcy]);

  chart.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");;

});

force
  .nodes(json.nodes)
  .links(json.links)
  .on("tick", tick)
  .start();

svg.call(zoom)

function tick(e) {
console.log(e)
  var k = 6 * e.alpha;

  // Push sources up and targets down to form a weak tree.
  link
    .each(function(d,i) {

      d.source.y -= k * 60, d.target.y += k * 100;
    /*
    if(i%2==1){
       d.source.x -=  0.4/k  
    }else{
        d.source.x +=  0.4/k  
    }
    */

    })
    .attr("x1", function(d) {
      return d.source.x;
    })
    .attr("y1", function(d) {
      return d.source.y;
    })
    .attr("x2", function(d) {
      return d.target.x;
    })
    .attr("y2", function(d) {
      return d.target.y;
    });

  node
    .attr("cx", function(d) {
      return d.x;
    })
    .attr("cy", function(d) {
      return d.y;
    });

}
  </script>

</body>
</html>
os8fio9y

os8fio9y2#

使用hack on tree布局-从其他节点绘制附加链接检查此示例
如果黑客是您正在寻找的解决方案,下面的实现可能会有所帮助。这是一个基于rob schmueckers博客多父节点d3.js示例的d3可折叠树,可以处理多个父链接和基本树事件。它通常分为两部分 index.html 构建html结构,一个样式表 style.css 而实际的剧本呢
tree.js index.html :
定义一个id为的div tree_view 后来拿着树。呼叫 tree.js 创建树。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>D3 collapsable multiple parents tree</title>
  <link rel="stylesheet" type="text/css" href="style.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
</head>

<body>
  <div id="tree_view"></div>       <!-- div holding tree -->
  <script src="tree.js"></script>  <!-- script to create tree -->
</body>

</html>
``` `style.css` :

tree_view {

width: 100%;
height: 100%;
margin-top: 30px;
}

/* basic node */
.node {
cursor: pointer;
text-anchor: start;
}

/* rectangle node */
.node rect {
stroke: gray;
stroke-width: 1.5px;
}

/* node text */
.node text {
font: 12px sans-serif;
}

/* standard links (.link) and multiparent links (.mpLink) */
.link, .mpLink {
fill: none;
stroke: #ccc;
}
``` tree.js :(完整代码如下)
基本上,您需要为要添加的每个附加链接定义一个新的链接对象。每个链接都需要一个源节点和目标节点。备份节点是事件处理所必需的:

let link = new Object();
link.source = pairNode1;
link.target = pairNode2;
link._source = pairNode1; // backup source
link._target = pairNode2; // backup target
additionalLinks.push(link)

现在,您可以处理更新过程中的所有附加链接( updateTree(source) ):

// ======== add additional links (mpLinks) ========
let mpLink = svg.selectAll("path.mpLink")
  .data(additionalLinks);

mpLink.enter().insert("path", "g")
  .attr("class", "mpLink")
  .attr("x", nodeWidth / 2)
  .attr("y", nodeHeight / 2)
  .attr("d", function (d) {
      var o = { x: source.x0, y: source.y0 };
        return diagonal({ source: o, target: o });
      });

mpLink.transition()
  .duration(duration)
  .attr("d", diagonal)
  .attr("stroke-width", 1.5)

mpLink.exit().transition()
  .duration(duration)
  .attr("d", function (d) {
      let o = { x: source.x, y: source.y };
         return diagonal({ source: o, target: o });
      })
      .remove();

并在方法中定义on事件行为 click(d) .
全部 tree.js 代码:

// plot properties
let root;
let tree;
let diagonal;
let svg;
let duration = 750;
let treeMargin = { top: 0, right: 20, bottom: 20, left: 20 };
let treeWidth = window.innerWidth - treeMargin.right - treeMargin.left;
let treeHeight = window.innerHeight - treeMargin.top - treeMargin.bottom;
let treeDepth = 5;
let maxTextLength = 90;
let nodeWidth = maxTextLength + 20;
let nodeHeight = 36;
let scale = 1;

// tree data
let data = [
    {
        "name": "Root",
        "parent": "null",
        "children": [
            {
                "name": "Level 2: A",
                "parent": "Top Level",
                "children": [
                    {
                        "name": "A1",
                        "parent": "Level 2: A"
                    },
                    {
                        "name": "A2",
                        "parent": "Level 2: A"
                    }
                ]
            },
            {
                "name": "Level 2: B",
                "parent": "Top Level"
            }
        ]
    }
];

// additional (multiparent) links data array
let additionalLinks = []

/**
 * Initialize tree properties
 * @param {Object} treeData 
 */
function initTree(treeData) {
    // init
    tree = d3.layout.tree()
        .size([treeWidth, treeHeight]);
    diagonal = d3.svg.diagonal()
        .projection(function (d) { return [d.x + nodeWidth / 2, d.y + nodeHeight / 2]; });
    svg = d3.select("div#tree_view")
        .append("svg")
        .attr("width", treeWidth + treeMargin.right + treeMargin.left)
        .attr("height", treeHeight + treeMargin.top + treeMargin.bottom)
        .attr("transform", `translate(${treeMargin.left},${treeMargin.top})scale(${scale},${scale})`);
    root = treeData[0];
    root.x0 = treeHeight / 2;
    root.y0 = 0;

    // fill additionalLinks array
    let pairNode1 = tree.nodes(root).filter(function(d) {
        return d['name'] === 'Level 2: B';
    })[0];
    let pairNode2 = tree.nodes(root).filter(function(d) {
        return d['name'] === 'A2';
    })[0];

    let link = new Object();
    link.source = pairNode1;
    link.target = pairNode2;
    link._source = pairNode1; // backup source
    link._target = pairNode2; // backup target
    additionalLinks.push(link)

    // update
    updateTree(root);
    d3.select(self.frameElement).style("height", "500px");

    // add resize listener
    window.addEventListener("resize", function (event) {
        resizeTreePlot();
    });
}

/**
 * Perform tree update. Update nodes and links
 * @param {Object} source
 */
function updateTree(source) {
    let i = 0;
    let nodes = tree.nodes(root).reverse();
    let links = tree.links(nodes);

    nodes.forEach(function (d) { d.y = d.depth * 80; });

    // ======== add nodes and text elements ========
    let node = svg.selectAll("g.node")
        .data(nodes, function (d) { return d.id || (d.id = ++i); });

    let nodeEnter = node.enter().append("g")
        .attr("class", "node")
        .attr("transform", function (d) { return `translate(${source.x0},${source.y0})`; })
        .on("click", click);

    nodeEnter.append("rect")
        .attr("width", nodeWidth)
        .attr("height", nodeHeight)
        .attr("rx", 2)
        .style("fill", function(d) { return d._children ? "#ace3b5": "#f4f4f9"; });

    nodeEnter.append("text")
        .attr("y", nodeHeight / 2)
        .attr("x", 13)
        .attr("dy", ".35em")
        .text(function (d) { return d.name; })
        .style("fill-opacity", 1e-6);

    let nodeUpdate = node.transition()
        .duration(duration)
        .attr("transform", function (d) { return `translate(${d.x},${d.y})`; });

    nodeUpdate.select("rect")
        .attr("width", nodeWidth)
        .style("fill", function(d) { return d._children ? "#ace3b5": "#f4f4f9"; });

    nodeUpdate.select("text").style("fill-opacity", 1);

    let nodeExit = node.exit().transition()
        .duration(duration)
        .attr("transform", function (d) { return `translate(${source.x},${source.y})`; })
        .remove();

    nodeExit.select("rect")
        .attr("width", nodeWidth)
        .attr("rx", 2)
        .attr("height", nodeHeight);

    nodeExit.select("text")
        .style("fill-opacity", 1e-6);

    // ======== add links ========
    let link = svg.selectAll("path.link")
        .data(links, function (d) { return d.target.id; });

    link.enter().insert("path", "g")
        .attr("class", "link")
        .attr("x", nodeWidth / 2)
        .attr("y", nodeHeight / 2)
        .attr("d", function (d) {
            var o = { x: source.x0, y: source.y0 };
            return diagonal({ source: o, target: o });
        });

    link.transition()
        .duration(duration)
        .attr("d", diagonal)

    link.exit().transition()
        .duration(duration)
        .attr("d", function (d) {
            let o = { x: source.x, y: source.y };
            return diagonal({ source: o, target: o });
        })
        .remove();

    // ======== add additional links (mpLinks) ========
    let mpLink = svg.selectAll("path.mpLink")
        .data(additionalLinks);

    mpLink.enter().insert("path", "g")
        .attr("class", "mpLink")
        .attr("x", nodeWidth / 2)
        .attr("y", nodeHeight / 2)
        .attr("d", function (d) {
            var o = { x: source.x0, y: source.y0 };
            return diagonal({ source: o, target: o });
        });

    mpLink.transition()
        .duration(duration)
        .attr("d", diagonal)
        .attr("stroke-width", 1.5)

    mpLink.exit().transition()
        .duration(duration)
        .attr("d", function (d) {
            let o = { x: source.x, y: source.y };
            return diagonal({ source: o, target: o });
        })
        .remove();

    nodes.forEach(function (d) {
        d.x0 = d.x;
        d.y0 = d.y;
    });
}

/**
 * Handle on tree node clicked actions
 * @param {Object} d node
 */
function click(d) {
    // update regular links
    if (d.children) {
        d._children = d.children;
        d.children = null;
    } else {
        d.children = d._children;
        d._children = null;
    }

    // update additional links
    additionalLinks.forEach(function(link){
        let sourceVisible = false;
        let targetVisible = false;
        tree.nodes(root).filter(function(n) {
            if(n["name"] == link._source.name){
                sourceVisible = true;
            }
            if(n["name"] == link._target.name){
                targetVisible = true;
            }
        });

        if(sourceVisible && targetVisible){
            link.source = link._source;
            link.target = link._target;
        }
        else if(!sourceVisible && targetVisible 
                    || !sourceVisible && !targetVisible){
            link.source = d;
            link.target = link.source;
        }
        else if(sourceVisible && !targetVisible){
            link.source = link._source;
            link.target = link.source;
        }
    });

    // define more links behavior here...

    updateTree(d);
}

/**
 * Update tree dimension
 */
 function updateTreeDimension() {
    tree.size([treeWidth, treeHeight]);
    svg.attr("width", treeWidth + treeMargin.right + treeMargin.left)
        .attr("height", treeHeight + treeMargin.top + treeMargin.bottom)
        .attr("transform", `translate(${treeMargin.left},${treeMargin.top})scale(${scale},${scale})`);
}

/**
 * Resize the tree using current window dimension
 */
function resizeTreePlot() {
    treeWidth = 0.9 * window.innerWidth - treeMargin.right - treeMargin.left;
    treeHeight = (treeDepth + 2) * nodeHeight * 2;
    updateTreeDimension();
    updateTree(root);
}

// plot tree
initTree(data);
updateTree(root);

在这里你可以找到完整的演示。你也可以用叉子叉这个项目。

相关问题