【问题标题】:Org Chart - Cluster Layout V3 to V4组织结构图 - 集群布局 V3 到 V4
【发布时间】:2017-08-21 06:59:49
【问题描述】:

我正在尝试更新 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,我真的很迷茫:-(。

【问题讨论】:

  • 你觉得我的回答怎么样?是你想要的吗?
  • 嗨@blackmiaool,我还需要做一些测试,抱歉耽搁了。就目前情况而言,您提供的代码与原始代码不同,但这就是为什么我会对其进行测试;-) 请耐心等待。
  • 我刚刚发现我什至没有注意到头像是可点击的。对不起……
  • 请随时更新您的答案,反正我现在正在做其他事情,所以我要到今天晚些时候才能测试 ;-)
  • 现在完成了...

标签: d3.js


【解决方案1】:

现场演示:

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

【讨论】:

    猜你喜欢
    • 2011-01-06
    • 1970-01-01
    • 2020-10-26
    • 2016-06-15
    • 2018-12-30
    • 2020-10-11
    • 2011-04-16
    • 2019-03-22
    相关资源
    最近更新 更多