Note that there are some explanatory texts on larger screens.

plurals
  1. POD3.js Zooming and panning a collapsible tree diagram
    primarykey
    data
    text
    <p>I'm using D3.js to plot a collapsible tree diagram like in the <a href="http://mbostock.github.io/d3/talk/20111018/tree.html" rel="noreferrer">example</a>. It's working mostly well, but the diagram might change dramatically in size when it enters its normal function (ie instead of the few nodes I have now, I'll have a <em>lot</em> more).</p> <p>I wanted to make the SVG area scroll, I've tried everything I found online to make it work, but with no success. The best I got working was using the <code>d3.behaviour.drag</code>, in which I drag the whole diagram around. It is far from optimal and glitches a lot, but it is kinda usable.</p> <p>Even so, I'm trying to clean it up a little bit and I realised the <code>d3.behaviour.zoom</code> can also be used to <em>pan</em> the SVG area, according to the API docs.</p> <p><strong>Question:</strong> Can anyone explain how to adapt it to my code? </p> <p>I would like to be able to <em>pan</em> the SVG area with the diagram, if possible making it react to some misuses, namely, trying to pan the diagram out of the viewport, and enabling to zoom to the maximum viewport's dimensions...</p> <p>This is my code so far:</p> <pre><code>var realWidth = window.innerWidth; var realHeight = window.innerHeight; function load(){ callD3(); } var m = [40, 240, 40, 240], w = realWidth -m[0] -m[0], h = realHeight -m[0] -m[2], i = 0, root; var tree = d3.layout.tree() .size([h, w]); var diagonal = d3.svg.diagonal() .projection(function(d) { return [d.y, d.x]; }); var vis = d3.select("#box").append("svg:svg") .attr("class","svg_container") .attr("width", w) .attr("height", h) .style("overflow", "scroll") .style("background-color","#EEEEEE") .append("svg:g") .attr("class","drawarea") .attr("transform", "translate(" + m[3] + "," + m[0] + ")") ; var botao = d3.select("#form #button"); function callD3() { //d3.json(filename, function(json) { d3.json("D3_NEWCO_tree.json", function(json) { root = json; d3.select("#processName").html(root.text); root.x0 = h / 2; root.y0 = 0; botao.on("click", function(){toggle(root); update(root);}); update(root); }); function update(source) { var duration = d3.event &amp;&amp; d3.event.altKey ? 5000 : 500; // Compute the new tree layout. var nodes = tree.nodes(root).reverse(); // Normalize for fixed-depth. nodes.forEach(function(d) { d.y = d.depth * 50; }); // 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("svg:g") .attr("class", "node") .attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; }) .on("click", function(d) { toggle(d); update(d); }); nodeEnter.append("svg:circle") .attr("r", function(d){ return Math.sqrt((d.part_cc_p*1))+4; }) .attr("class", function(d) { return "level"+d.part_level; }) .style("stroke", function(d){ if(d._children){return "blue";} }) ; nodeEnter.append("svg:text") .attr("x", function(d) { return d.children || d._children ? -((Math.sqrt((d.part_cc_p*1))+6)+this.getComputedTextLength() ) : Math.sqrt((d.part_cc_p*1))+6; }) .attr("y", function(d) { return d.children || d._children ? -7 : 0; }) .attr("dy", ".35em") .attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; }) .text(function(d) { if(d.part_level&gt;0){return d.name;} else if(d.part_multi&gt;1){return "Part " + d.name+ " ["+d.part_multi+"]";} else{return "Part " + d.name;} }) .attr("title", function(d){ var node_type_desc; if(d.part_level!=0){node_type_desc = "Labour";}else{node_type_desc = "Component";} return ("Part Name: "+d.text+"&lt;br/&gt;Part type: "+d.part_type+"&lt;br/&gt;Cost so far: "+d3.round(d.part_cc, 2)+"&amp;euro;&lt;br/&gt;"+"&lt;br/&gt;"+node_type_desc+" cost at this node: "+d3.round(d.part_cost, 2)+"&amp;euro;&lt;br/&gt;"+"Total cost added by this node: "+d3.round(d.part_cost*d.part_multi, 2)+"&amp;euro;&lt;br/&gt;"+"Node multiplicity: "+d.part_multi); }) .style("fill-opacity", 1e-6); // Transition nodes to their new position. var nodeUpdate = node.transition() .duration(duration) .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; }); nodeUpdate.select("circle") .attr("r", function(d){ return Math.sqrt((d.part_cc_p*1))+4; }) .attr("class", function(d) { return "level"+d.part_level; }) .style("stroke", function(d){ if(d._children){return "blue";}else{return null;} }) ; 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) { return "translate(" + source.y + "," + source.x + ")"; }) .remove(); nodeExit.select("circle") .attr("r", function(d){ return Math.sqrt((d.part_cc_p*1))+4; }); 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("svg:path", "g") .attr("class", "link") .attr("d", function(d) { var o = {x: source.x0, y: source.y0}; return diagonal({source: o, target: o}); }) .transition() .duration(duration) .attr("d", diagonal); // Transition links to their new position. link.transition() .duration(duration) .attr("d", diagonal); // Transition exiting nodes to the parent's new position. link.exit().transition() .duration(duration) .attr("d", function(d) { var o = {x: source.x, y: source.y}; return diagonal({source: o, target: o}); }) .remove(); $('svg text').tipsy({ fade:true, gravity: 'nw', html:true }); // Stash the old positions for transition. nodes.forEach(function(d) { d.x0 = d.x; d.y0 = d.y; }); var drag = d3.behavior.drag() .origin(function() { var t = d3.select(this); return {x: t.attr("x"), y: t.attr("y")}; }) .on("drag", dragmove); d3.select(".drawarea").call(drag); } // Toggle children. function toggle(d) { if (d.children) { d._children = d.children; d.children = null; } else { d.children = d._children; d._children = null; } } function dragmove(){ d3.transition(d3.select(".drawarea")) .attr("transform", "translate(" + d3.event.x +"," + d3.event.y + ")"); } } </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload