d3.js - 组织结构图 - 集群布局 V3 至 V4

标签 d3.js

我正在尝试更新this Org Chart visualisation使用 d3 的 V4 但我遇到了问题。 This JSFiddle使用 V3 而 this JSFiddle使用V4。

我遇到了一些变化,例如 CSV 解析(第 53-54 行):

// var data = d3.csv.parse(csvData);
var data = d3.csvParse(csvData);

以及对角线的计算(讨论 here ,第 79-97 行):

/*
var diagonal = d3.svg.diagonal.radial()
    .projection(function(d) {
        return [d.y, d.x / 180 * Math.PI];
    });
*/
var diagonal = function n(n, i) {
    var u = t.call(this, n, i),
        o = e.call(this, n, i),
        a = (u.y + o.y) / 2,
        l = [u, {
            x: u.x,
            y: a
        }, {
            x: o.x,
            y: a
        }, o];
    return l = l.map(r), "M" + l[0] + "C" + l[1] + " " + l[2] + " " + l[3]
}

节点的获取发生了变化(第211-212行):

//var nodes = cluster.nodes(root);
var nodes = d3.hierarchy(root);

现在我似乎遇到了这些节点的问题,并在第 216 行显示此错误消息:

Uncaught TypeError: cluster.links is not a function

非常感谢任何帮助,在 JS 方面我并不马虎,但这是我第一次涉足 d3,我真的很迷失:-(。

最佳答案

现场演示:

var csvData = `Associate,Manager
Matt Herman,John Smith
Jane Doe,John Smith
Adam Brown,John Smith
Susan Harris,John Smith
Mike Jones,John Smith
John Smith,Colin Krauss
Colin Krauss,Ashley Carlin
Ashley Carlin,Lia McDermott
Evan Park,Lia McDermott
Lauren Werner,Evan Park
Shane Waterson,Evan Park
Emma Smith,Evan Park
Mike Gregory,Evan Park
Jose Biggleman,Evan Park
Michelle Spektor,Evan Park
Juan Branch,Evan Park
John Orbase,Evan Park
Matt McCloud,Evan Park
Kelsey Carsen,Evan Park
Kelli Krazwinski,Colin Krauss
Stephanie Goldstien,Colin Krauss
Ryan Woolwine,Colin Krauss
Kyle Bohm,Colin Krauss
Sydney Yellen,Colin Krauss
Shankar Murjhree,Colin Krauss
Wayne Ellington,Colin Krauss
Dwight Folds,Colin Krauss
Ellen McGlynn,Colin Krauss
Nicolas Smith,Colin Krauss
Molly Ercole,Colin Krauss
Scott Hane,Colin Krauss
Regina McMahon,Colin Krauss
Skip Holden,Colin Krauss
Kadeem McPherson,Colin Krauss
Ray Ortiz,Colin Krauss
Janet Barnes,Colin Krauss
Holly Gold,Colin Krauss
Lance Martinez,Ashley Carlin
Mike Lubow,Ashley Carlin
Jordan Belsin,Ashley Carlin
Tom Strithers,Ashley Carlin
Jamie Raleigh,Ellen McGlynn
Joseph Bowman,Ellen McGlynn
Kylie Branch,Ellen McGlynn
Lars Randall,Ellen McGlynn
Carlos Barndt,Lia McDermott
Leo Hastings,Lia McDermott
Jaime Kellemen,Lia McDermott
Harvey Klien,Lia McDermott
Lia McDermott,Lia McDermott`;


var data = d3.csvParse(csvData);

var height = document.getElementById("tree-container").offsetHeight;
var width = document.getElementById("tree-container").offsetWidth;
var avatarRadius = 20;
var translateOffset = 25;
var radius = d3.min([height, width]) / 2;
var cluster = d3.cluster()
    .size([360, radius / 1.33])
    // .separation(function(a,b){return (a.parent == b.parent ? 1:2)/a.depth;});

var svg = d3.select("#tree-container").append("svg")
    .attr("width", radius * 2)
    .attr("height", radius * 2)
    .attr("id", "tree-container-svg")
    .append("g")
    .attr("transform", "translate(" + radius + "," + height / 2 + ")");

//Clip path needed for cicrular SVG avatars
var defs = svg.append('defs');
var clipPath = defs.append('clipPath')
    .attr('id', 'clip-circle')
    .append('circle')
    .attr('r', avatarRadius - 2.5);

function project(x, y) {
        var angle = (x - 90) / 180 * Math.PI,
            radius = y;
        return [radius * Math.cos(angle), radius * Math.sin(angle)];
}
var diagonal = function (d) {
    return "M" + project(d.x, d.y) + "C" + project(d.x, (d.y + d.parent.y) / 2) +
        " " + project(d.parent.x, (d.y + d.parent.y) / 2) +
        " " + project(d.parent.x, d.parent.y);
}
d3.selection.prototype.moveToFront = function () {
    return this.each(function () {
        this.parentNode.appendChild(this);
    });
};

d3.selection.prototype.moveToBack = function () {
    return this.each(function () {
        var firstChild = this.parentNode.firstChild;
        if (firstChild) {
            this.parentNode.insertBefore(this, firstChild);
        }
    });
};

//http://www.d3noob.org/2014/01/tree-diagrams-in-d3js_11.html
function treeify(list, callback) {

    var dataMap = list.reduce(function (map, node) {
        map[node.Associate] = node;
        return map;
    }, {});

    var treeData = [];
    list.forEach(function (node) {
        //Assuming the highest node is the last in the csv file
        if (node.Manager === node.Associate) {
            node.Manager = "Board of Directors"
            callback(node);
        }
        // add to parent
        var parent = dataMap[node.Manager];

        if (parent) {
            // create child array if it doesn't exist
            (parent.children || (parent.children = []))
            // add node to child array
            .push(node);
        } else {
            // parent is null or missing
            treeData.push(node);
        }
    });
};

function findItem(root, name, callback) {
    var stack = [];
    stack.push(root);
    while (stack.length !== 0) {
        var element = stack.pop();
        if (element.Associate === name) {
            callback(element);
            return;
        }
        //The up, uncompressed case
        else if (element.children !== undefined && element.children.length > 0) {
            for (var i = 0; i < element.children.length; i++) {
                stack.push(element.children[i]);
            }
        }
        //The down (compressed) case
        else if (element._children !== undefined && element._children.length > 0) {
            for (var j = 0; j < element._children.length; j++) {
                stack.push(element._children[j]);
            }
        }
    }
}

function defaultPlot(root, elem) {
    findItem(root, elem, function (d) {
        //Showing 1 up and below
        findItem(root, d.Manager, function (x) {
            (x.children) ? x.children.forEach(collapse): x.children = x._children;
            drawIt(x, root);
        })
    })
}

function collapse(d) {
    if (d.children) {
        d._children = d.children;
        d._children.forEach(collapse);
        d.children = undefined;
    }
}

//For the buggy transition interruption with many nodes
function showAllCurrentPathsAndNodes() {
    d3.selectAll(".link").style("opacity", 1);
    d3.selectAll(".node").style("opacity", 1);
}

// Toggle children on click.
function clickedNode(d, root) {
    //Accounting for the transition bug on the delay   
    showAllCurrentPathsAndNodes();

    if (d.children) {
        d._children = d.children;
        d.children = undefined;
        drawIt(root)
    } else {
        d.children = d._children;
        d._children = undefined;
        drawIt(root)
    }
}

//http://bl.ocks.org/syntagmatic/4092944
function drawIt(root) {    
    var nodes = d3.hierarchy(root);
    cluster(nodes);

    var links = nodes.descendants().slice(1);
  
    var link = svg.selectAll("path.link").data(links);

    var node = svg.selectAll("g.node").data(nodes.descendants(),function(d){
        return d.data.Associate;
    });


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


    d3.selectAll(".node-cicle").classed("highlight", false);

    showAllCurrentPathsAndNodes();
    

    link.enter().append("path")
        .attr("class", "link")
        .attr("d", diagonal)
        .attr("", function (d) {
            d3.select(this).moveToBack();
        })
        .style("opacity", 0)
        .transition()
        .duration(300)
        .delay(function (d, i) {
            return 28 * i;
        }).style("opacity", 1);

    node.transition().duration(800).attr("transform", function (d) {
        return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")";
    });

    var g = node.enter().append("g")
        .attr("class", "node")
        .attr("transform", function (d) {
            return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")";
        })
        .style("opacity", 0)
        .style("cursor", function (d) {
            d=d.data;
            return ((d._children || d.children) && d.Manager !== "Board of Directors") ? "pointer" : "not-allowed";
        })
        .on("mouseover", function () {
            d3.select(this).moveToFront();
        })

    //Cant trust the enter append here, reassign the event listener for all nodes each draw
    d3.selectAll(".node")
        .on("click", function (d) {
            d=d.data;
            return ((d._children || d.children) && d.Manager !== "Board of Directors") ? clickedNode(d, root) : "";
        });


    g.transition().duration(300)
        .delay(function (d, i) {
            return 28 * i;
        })
        .style("opacity", 1);

    g.append("circle")
        .attr("r", avatarRadius)
        .attr("class", "circle-marker")
        .style("stroke", function (d) {
            d = d.data;
            return ((d._children || d.children) && d.Manager !== "Board of Directors") ? "steelblue" : "gray";
        })
        .style("fill", function (d) {
            d = d.data;
            return ((d._children || d.children) && d.Manager !== "Board of Directors") ? "steelblue" : "#fff";
        });

    g.append("svg:image")
        .attr("class", "node-avatar")
        .attr("xlink:href", "http://safariuganda.com/wp-content/uploads/2014/12/480px-Facebook-default-no-profile-pic.jpg")
        .attr("height", avatarRadius * 2)
        .attr("width", avatarRadius * 2)
        .attr("x", "-" + avatarRadius)
        .attr("y", "-" + avatarRadius)
        .attr('clip-path', 'url(#clip-circle)');

    //Might want to tween this?
    d3.selectAll(".node-avatar")
        .attr("transform", function (d) {
            return "rotate(" + (-1 * (d.x - 90)) + ")";
        });

    g.append("text")
        .attr("dy", ".31em")
        .attr("class", "label-text")
        .text(function (d) {
            return d.data.Associate;
        })

    //search all labels to ensure they are right side up (cant rely on the enter append here)
    d3.selectAll(".label-text")
        .attr("text-anchor", function (d) {
            return d.x < 180 ? "start" : "end";
        })
        .attr("transform", function (d) {
            return d.x < 180 ? "translate(" + translateOffset + ")" : "rotate(180)translate(-" + translateOffset + ")";
        })

    link.exit().transition().duration(0).style("opacity", 0).remove();
    node.exit().transition().duration(0).style("opactiy", 0).remove();

}

treeify(data, function (treeReturn) {
    var root = treeReturn;
    defaultPlot(root, root.children[0].Associate)
});
html,
body {
    font-family: 'Open Sans', sans-serif;
    font-size: 12px;
    background-color: #fff;
    height: 100%;
    width: 100%;
    background-color: #f1f1f1;
    position: relative;
    display: block;
}

#tree-container {
    position: relative;
    display: block;
    margin-left: 100px;
    height: 100%;
    width: 100%;
}

.node circle {
    stroke-width: 1.5px;
}

.node {
    font: 10px sans-serif;
}

.link {
    fill: none;
    stroke: #ccc;
    stroke-width: 1.5px;
}

.label-text {
    -webkit-user-select: none;
    /* Chrome/Safari */
    -moz-user-select: none;
    /* Firefox */
    -ms-user-select: none;
    /* IE10+ */
    /* Rules below not implemented in browsers yet */
    -o-user-select: none;
    user-select: none;
}
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <title>test</title>

</head>

<body>
    <div id="tree-container"></div>
    <link rel="stylesheet" href="style.css">
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <script src="index.js"></script>
</body>

</html>

如何迁移

var diagonal = d3.svg.diagonal.radial()
.projection(function(d) {
    return [d.y, d.x / 180 * Math.PI];
});

function project(x, y) {
    var angle = (x - 90) / 180 * Math.PI, radius = y;
    return [radius * Math.cos(angle), radius * Math.sin(angle)];
}

var diagonal = function (d) {
    return "M" + project(d.x, d.y) + "C" + project(d.x, (d.y + d.parent.y) / 2) +
    " " + project(d.parent.x, (d.y + d.parent.y) / 2) +
    " " + project(d.parent.x, d.parent.y);
}

而且 drawIt 方法也有很多变化。只需引用https://bl.ocks.org/mbostock/4739610f6d96aaad2fb1e78a72b385ab

关于d3.js - 组织结构图 - 集群布局 V3 至 V4,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43065565/

相关文章:

d3.js - 在 svg 上居中对齐饼图

javascript - 如何使用 D3 制作三重条形图?

javascript - 未捕获的类型错误 : Failed to execute 'serializeToString' on 'XMLSerializer' : Failing to download an svg (created with D3. js) 为 png/pdf

javascript - Windows 8 上的 Safari 5 反转 SVG 上的所有文本元素

javascript - 如何创建交互式 d3 折线图以在悬停时显示数据图/标签

d3.js - D3 : Convert scale level of the time scale to time period, 就像 : day, 周或月?

javascript - 鼠标悬停在事件线路径 d3 上

d3.js - 防止模板丢弃 DOM 元素

javascript - 在可拖动点与控制点之间绘制曲线以调整曲线 SVG 和 d3.js

javascript - 引用错误: barChartHelper is not defined?