1
votes

I m trying to find out a way to add auto-zoom to the current code. The usual zoom panning is working fine. However, when the number of nodes in a tree is high, it got cut off by the corners. Is there anyway to zoom automatically as soon the tree passed the boundary of the width and height of the svg. Maybe based on the depth size or the total number of nodes.

function buildTree(treeData){

  var contextMenuList = [
    {
      title: 'Remove Node',
      action: function(elm, d, i) {

        if (d.parent && d.parent.children){
          var nodeToDelete = _.where(d.parent.children, {name: d.name});
          if (nodeToDelete){
            d.parent.children = _.without(d.parent.children, nodeToDelete[0]);
          }
          update(d);
        }
      }
    },
    {
      title: 'Synopsis',
      action: function(elm, d, i) {

        console.log("Option 2 clicked");
        console.log("Node Name: "+ d.name);
        setNodeTopic(d.name);
      }
    }
  ];

  var margin = {top:40, right:120,bottom:20,left:20};
  var width = 960 - margin.right - margin.left;
  var height = 900 - margin.top - margin.bottom;

  var i = 0,duration = 750;
    //refers to the tree itself
  var tree = d3.layout.tree()
    .size([height,width])
    .nodeSize([100,100]);

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

  //refers to the rectangle outside
  var zm;
  var svg = d3.select("#tree").append("svg")
    .attr("width",width+margin.right+margin.left)
    .attr("height",height+margin.top+margin.bottom)
    .append("svg:g")
    .attr("transform","translate("+margin.left+","+margin.top+")")
    .call(zm = d3.behavior.zoom().scaleExtent([0.5,2]).on("zoom", redraw)).append("g")
    .attr("transform","translate("+400+","+50+")");

  zm.translate([400,20]);

  var root = treeData;


  function autoOpen(head, time) {
    window.setTimeout(function() {
      nodeclick(head); //do node click
      if (head._children) {
        //if has children
        var timeOut = 1000; //set the timout variable
        head._children.forEach(function(child) {
          autoOpen(child, timeOut); //open the child recursively
          timeOut = timeOut + 1000;
        })
      }
    }, time);
  }

  autoOpen(root,1000);

  update(root);

  function update(source){
    var nodes = tree.nodes(root).reverse(),
      links = tree.links(nodes);

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

    var node = svg.selectAll("g.node")
      .data(nodes,function(d){
        return d.id || (d.id = ++ i);
      });

    var nodeEnter = node.enter().append("svg:g")
      .attr("class","node")
      .attr("transform",function(d){
        if(!source.x0 && !source.y0)
          return "";
        return "translate("+source.x0 + "," + source.y0 + ")";
      })
      .on("click",nodeClick)
      .on('contextmenu', d3.contextMenu(contextMenuList));

    nodeEnter.append("circle")
      .attr("r",50)
      .attr("stroke",function(d){
        return d.children || d._children ? "steelblue" : "#00c13f";
      })
      .style("fill",function(d){
        return d.children || d._children ? "lightsteelblue" : "#fff";
      })

    nodeEnter.append("text")
      .attr("y",function(d){
        //return d.children || d._children ? -18 : 18;
        return -10;
      })
      .attr("dy",".35em")
      .attr("text-anchor","middle")
      .style("fill-opacity",1e-6)
      .each(function (d) {
        var arr = d.name.split(" ");
        for (i = 0; i < arr.length; i++) {
          d3.select(this).append("tspan")
            .text(arr[i])
            .attr("dy", i ? "1.2em" : 0)
            .attr("x", 0)
            .attr("text-anchor", "middle")
            .attr("class", "tspan" + i);
        }
      });

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

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

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

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

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

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


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

    link.enter().insert("svg:path","g")
      .attr("class","link")
      .attr("d",function(d){
        if(!source.x0 && !source.y0)
          return "";
        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){
        var 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;
    });
  }

  function nodeClick(d) {
    if (d.children) {
      d._children = d.children;
      d.children = null;
    } else {
      d.children = d._children;
      d._children = null;
    }
    update(d);
  }

  function redraw() {
    svg.attr("transform",
      "translate(" + d3.event.translate + ")"
      + " scale(" + d3.event.scale + ")");
  }
}

buildTree(that.objectList);
};
1

1 Answers

0
votes

My solution to this problem is by using viewbox. Making the svg responsible allows the svg to be able to resize based on browser window size.

Here is an example: Making SVG Responsive