组织结构图 - 群集布局V3到V4

时间:2017-03-28 09:20:27

标签: 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并且我真的很失落: - (。

1 个答案:

答案 0 :(得分:2)

现场演示:



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)
});
&#13;
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;
}
&#13;
<!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>
&#13;
&#13;
&#13;

如何迁移

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