How to change orientation of a D3 tree layout by 9

2019-02-05 09:22发布

I just started getting involved in web visualizations, so I'm totally novice. My goal is to display a family tree where a root node would have both multiple parents and children. While looking for a solution I found this example: http://bl.ocks.org/jdarling/2503502 It's great because it seems to have the feature I need. However, I would like to alter the orientation (top-to-bottom). I tried to do so using this example: http://bl.ocks.org/mbostock/3184089 but failed.

My code:

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

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

var elbow = function (d, i){
    var source = calcTop(d.source);
    var target = calcTop(d.target);
    var hx = (target.x-source.x)/2;
    if(d.isRight)
        hx = -hx;
    return  "M" + source.x + "," + source.y
          + "H" + (source.x+hx)
          + "V" + target.y + "H" + target.x;
};

var connector = elbow;

var calcTop = function(d){
    var top = d.x;
    if(!d.isRight){
        top = d.x-halfHeight;
        top = halfHeight - top;
    }
    return {x : top, y : d.y};
};

var vis = d3.select("#chart")
    .append("svg")
    .attr("height", height + margin.top + margin.bottom)
    .attr("width", width + margin.right + margin.left)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

d3.json("tree.json", function(json) {
    root = json;
    root.x0 = height / 2;
    root.y0 = width / 2;
    var t1 = d3.layout.tree()
        .size([halfHeight, width])
        .children(function(d){
            return d.winners;
        });
    var t2 = d3.layout.tree()
        .size([halfHeight, width])
        .children(function(d){
            return d.challengers;
        });
    t1.nodes(root);
    t2.nodes(root);

    var rebuildChildren = function(node){
        node.children = getChildren(node);
        if(node.children)
            node.children.forEach(rebuildChildren);
    }
    rebuildChildren(root);
    root.isRight = false;
    update(root);
});

var toArray = function(item, arr){
    arr = arr || [];
    var i = 0, l = item.children?item.children.length:0;
    arr.push(item);
    for(; i < l; i++){
        toArray(item.children[i], arr);
    }
    return arr;
};

function update(source) {
// Compute the new tree layout.
var nodes = toArray(source);

// Normalize for fixed-depth.
nodes.forEach(function(d) { d.x = d.depth * 180 + halfHeight; });

// Update the nodes…
var node = vis.selectAll("g.node")
    .data(nodes, function(d) { return d.id || (d.id = ++i); });

// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
    .attr("class", "node")
    .attr("transform", function(d) {
        return "translate(" + source.x0 + "," + source.y0 + ")";
    })
    .on("click", click);

nodeEnter.append("circle")
    .attr("r", 1e-6)
    .style("fill", function(d) {
        return d._children ? "lightsteelblue" : "#fff";
    });

nodeEnter.append("text")
    .attr("dy", function(d) { return d.isRight?14:-8;})
    .attr("text-anchor", "middle")
    .text(function(d) { return d.name; })
    .style("fill-opacity", 1e-6);

// Transition nodes to their new position.
var nodeUpdate = node.transition()
    .duration(duration)
    .attr("transform", function(d) {
        p = calcTop(d);
        return "translate(" + p.x + "," + p.y + ")";
});

nodeUpdate.select("circle")
    .attr("r", 4.5)
    .style("fill", function(d) {
        return d._children ? "lightsteelblue" : "#fff";
    });

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

// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
    .duration(duration)
    .attr("transform", function(d) {
         p = calcTop(d.parent||source);
         return "translate(" + p.x + "," + p.y + ")";
    })
    .remove();

nodeExit.select("circle")
    .attr("r", 1e-6);

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

// Update the links...
var link = vis.selectAll("path.link")
    .data(tree.links(nodes), function(d) { return d.target.id; });

// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
    .attr("class", "link")
    .attr("d", function(d) {
        var o = {x: source.x0, y: source.y0};
        return connector({source: o, target: o});
    });

// Transition links to their new position.
link.transition()
    .duration(duration)
    .attr("d", connector);

// Transition exiting nodes to the parent's new position.
link.exit()
    .transition()
    .duration(duration)
    .attr("d", function(d) {
        var o = calcTop(d.source||source);
        if(d.source.isRight)
            o.x -= halfHeight - (d.target.x - d.source.x);
        else
            o.x += halfHeight - (d.target.x - d.source.x);
        return connector({source: o, target: o});
    })
    .remove();

// Stash the old positions for transition.
nodes.forEach(function(d) {
    var p = calcTop(d);
    d.x0 = p.x;
    d.y0 = p.y;
});

// Toggle children on click.
function click(d) {
    if (d.children) {
        d._children = d.children;
        d.children = null;
    } else {
        d.children = d._children;
        d._children = null;
    }
update(source);

} }

Would really appreciate the help!

3条回答
祖国的老花朵
2楼-- · 2019-02-05 09:43

Here is an example of bottom-top diagram that I made. I gather it might be useful for you, maybe you'll spot some idea etc.

enter image description here

Link to code on codepen. Feel free to ask any question.

查看更多
闹够了就滚
3楼-- · 2019-02-05 09:47

The example you're looking at is actually already flipped - this might be causing you some confusion. Trees in D3 are naturally top-down trees, and the code does a lot of x-y flipping to make the tree sideways.

Changing

  var nodeUpdate = node.transition()
      .duration(duration)
      .attr("transform", function(d) { p = calcLeft(d); return "translate(" + p.y + "," + p.x + ")"; })
      ;

to

  var nodeUpdate = node.transition()
      .duration(duration)
      .attr("transform", function(d) { p = calcLeft(d); return "translate(" + p.x + "," + p.y + ")"; })
      ;

will get the nodes displaying in the right position. Doing a similar change with any instance of swapped x-y coordinates inside update() fixes most of the positioning issues. One last thing is the elbow function/variable/whatever you want to call it, where

  return "M" + source.y + "," + source.x
         + "H" + (source.y+hy)
         + "V" + target.x + "H" + target.y;

should be changed to

  return "M" + source.x + "," + source.y
         + "V" + (source.y+hy)
         + "H" + target.x + "V" + target.y;

This changes the connector shape from horizontal vertical horizontal to vertical horizontal vertical. Note that this is a raw SVG line, not d3 at all. The changes I made (plus swapping width and height, and changing the AJAX JSON request to hardcoding the data - AJAX is hard to get working in fiddle) are all at http://jsfiddle.net/Zj3th/2/.

If you have no experience with d3 and SVG, I would definitely take a look and fully understand a simple example like http://blog.pixelingene.com/2011/07/building-a-tree-diagram-in-d3-js/ before you go further in modifying the code.

查看更多
\"骚年 ilove
4楼-- · 2019-02-05 09:50
its very easy to make rotation in d3 collapsible tree TOP-DOWN..

step 1: specify top,bottom,left,right part of our drawing pan and also the width and                            
        height.
Step 2: Make the orientation as top to bottom 
step 3: Reading the json and make the orientation
step 4: Appending the link and circle to the body

<script>
var jdata='${data}'
    var margin = {top: 20, right: 120, bottom: 20, left: 120},
    width = 980 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

    var orientations = {
      "top-to-bottom": {
        size: [width, height],
        x: function(d) { return d.x; },
        y: function(d) { return d.y; }
      }
    };

    var svg = d3.select("body").selectAll("svg")
        .data(d3.entries(orientations))
        .enter().append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    d3.json("resources/grap.json", function(json) {
         root = JSON.parse(jdata);

      svg.each(function(orientation) {
        var svg = d3.select(this),
            o = orientation.value;

        // Compute the layout.
        var tree = d3.layout.tree().size(o.size),
            nodes = tree.nodes(root),
            links = tree.links(nodes);

        // Create the link lines.
        svg.selectAll(".link")
            .data(links)
          .enter().append("path")
            .attr("class", "link")
            .attr("d", d3.svg.diagonal().projection(function(d) { return [o.x(d), o.y(d)]; }));

        // Create the node circles.
        svg.selectAll(".node")
            .data(nodes)
         .enter().append("circle")
            .attr("r",1.5)
            .attr("x", o.x)
            .attr("y", o.y)

      });
    });

</script>
查看更多
登录 后发表回答