1

There is similar question on SO, but I simply do not know how to use it in my case. I am trying to add zoom/pan capability to a force diagram. My knowledge with D3.js is basic, please forgive my silly question.

This is the original fiddle from this website.

And this fiddle is my attempt to add a zoom ability.

I have added this code:

  var zoom = d3.behavior.zoom()
        .scaleExtent([1, 10])
        .on("zoom", zoomed);

var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.call(zoom);


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

    function dragstarted(d) {
      d3.event.sourceEvent.stopPropagation();

      d3.select(this).classed("dragging", true);
      force.start();
    }

    function dragged(d) {

      d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = 
     d3.event.y);

    }

    function dragended(d) {

      d3.select(this).classed("dragging", false);
    }

And nothing is happening, code works but zoom and pan does not. What am I missing?

altocumulus
  • 21,179
  • 13
  • 61
  • 84
Amiga500
  • 5,874
  • 10
  • 64
  • 117
  • Possible duplicate of [Is there a way to zoom into a D3 force layout graph?](http://stackoverflow.com/questions/7871425/is-there-a-way-to-zoom-into-a-d3-force-layout-graph) – Mehdi Jan 13 '16 at 08:44
  • Have you had a look at this demo http://bl.ocks.org/mbostock/6123708 ? There are plenty of examples online for zoomable d3 force diagrams... – Mehdi Jan 13 '16 at 08:45

1 Answers1

5

You need to append a group to your SVG using .append('svg:g'); after declaring your SVG. Unfortunately you cannot apply transforms to the svg element directly when it has child elements (as they need to scale also).

You can see it in action here - http://jsfiddle.net/s7aLv276/ <- New Version

//Append a SVG to the body of the html page. Assign this SVG as an object to svg
    var svg = d3.select('body').append("svg")
      .attr("width", width)
      .attr("height", height)
      .call(zoom)
      .append('svg:g'); // <- Add your group so you can transform all elements together

The entire code:

 //Constants for the SVG
var width = 500,
  height = 500;

//Set up the colour scale
var color = d3.scale.category20();

//Set up the force layout
var force = d3.layout.force()
  .charge(-120)
  .linkDistance(80)
  .size([width, height]);

var zoom = d3.behavior.zoom()
  .scaleExtent([1, 10])
  .on("zoom", zoomed);

//Append a SVG to the body of the html page. Assign this SVG as an object to svg
var svg = d3.select('body').append("svg")
  .attr("width", width)
  .attr("height", height)
  .call(zoom)
  .append('svg:g');


//Read the data from the mis element 
var mis = document.getElementById('mis').innerHTML;
graph = JSON.parse(mis);

force.drag().on("dragstart", function() { d3.event.sourceEvent.stopPropagation(); });

//Creates the graph data structure out of the json data
force.nodes(graph.nodes)
  .links(graph.links)
  .start();


//Create all the line svgs but without locations yet
var link = svg.selectAll(".link")
  .data(graph.links)
  .enter().append("line")
  .attr("class", "link")
  .style("marker-end", "url(#suit)") //Added 
;

//Do the same with the circles for the nodes - no 
var node = svg.selectAll(".node")
  .data(graph.nodes)
  .enter().append("circle")
  .attr("class", "node")
  .attr("r", 8)
  .style("fill", function(d) {
    return color(d.group);
  })
  .call(force.drag);

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

function dragstarted(d) {
  d3.event.sourceEvent.stopPropagation();

  d3.select(this).classed("dragging", true);
  force.start();
}

function dragged(d) {

  d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y);

}

function dragended(d) {

  d3.select(this).classed("dragging", false);
}

//Now we are giving the SVGs co-ordinates - the force layout is generating the co-ordinates which this code is using to update the attributes of the SVG elements
force.on("tick", function() {
  link.attr("x1", function(d) {
      return d.source.x;
    })
    .attr("y1", function(d) {
      return d.source.y;
    })
    .attr("x2", function(d) {
      return d.target.x;
    })
    .attr("y2", function(d) {
      return d.target.y;
    });

  node.attr("cx", function(d) {
      return d.x;
    })
    .attr("cy", function(d) {
      return d.y;
    });
});

//---Insert-------
svg.append("defs").selectAll("marker")
  .data(["suit", "licensing", "resolved"])
  .enter().append("marker")
  .attr("id", function(d) {
    return d;
  })
  .attr("viewBox", "0 -5 10 10")
  .attr("refX", 25)
  .attr("refY", 0)
  .attr("markerWidth", 6)
  .attr("markerHeight", 6)
  .attr("orient", "auto")
  .append("path")
  .attr("d", "M0,-5L10,0L0,5 L10,0 L0, -5")
  .style("stroke", "#4679BD")
  .style("opacity", "0.6");
//---End Insert---

* Updated to add pan/zoom on nodes *

It looks like we weren't attaching a drag handler to the Force object. I've added the following line:

force.drag().on("dragstart", function() { d3.event.sourceEvent.stopPropagation(); });

The graph should allow you to now drag individual nodes, as well as pan and zoom the entire graph.

See it in action - http://jsfiddle.net/s7aLv276/

Garbit
  • 5,805
  • 6
  • 39
  • 72
  • 1
    Thats does makes it zoom and pan :). But for some reason draggin the node to new position is connected to zoom now. Before I could grab the node and take it to new position, but now when I try to do it it pans. Could you please take a look at it? – Amiga500 Jan 13 '16 at 09:12
  • 1
    Ah, I see. I've updated the fiddle so you can now pan and zoom on the graph and an individual node (see answer above) - http://jsfiddle.net/s7aLv276/ – Garbit Jan 27 '16 at 08:41