Separating zoom behaviour and drag behaviour in D3.js

I’m having troubles in separating the zoom and drag behaviour in a d3.js rendered svg force directed layout graph.

I’m following many of the Mike Bostock‘s examples like this that talks about this separation using

d3.event.sourceEvent.stopPropagation();

in the function where the drag behaviour (for nodes) is defined to stop the zoom event but uselessy.
What am I doing wrong?

Here’s the GitHub project page and a “live example” of the script: http://toniogela.org/


Here’s the script:

var width = window.innerWidth;
var height = window.innerHeight;

var svg = d3.select("body").append("svg");

d3.json("graph.json", function(error, graph) {

  graph.links.forEach(function(d) {
    d.source = graph.nodes[d.source];
    d.target = graph.nodes[d.target];
  });

  //LINKS
  var links = svg.append("g")
    .attr("class", "link")
    .selectAll("line")
    .data(graph.links)
    .enter()
    .append("g");

  links.append("line")
    .attr("x1", function(d) {
      return d.source.x * width / 100;
    })
    .attr("y1", function(d) {
      return d.source.y * height / 100;
    })
    .attr("x2", function(d) {
      return d.target.x * width / 100;
    })
    .attr("y2", function(d) {
      return d.target.y * height / 100;
    });

  links.append("text")
    .attr("text-anchor", "middle")
    .attr("x", function(d) {
      return (d.target.x + d.source.x) * width / 200;
    })
    .attr("y", function(d) {
      return (d.target.y + d.source.y) * height / 200;
    })
    .attr("dy", ".35em")
    .text(function(d) {
      return d.label;
    })
    .call(getBB);

  function getBB(selection) {
    selection.each(function(d) {
      d.bbox = this.getBBox();
    });
  }

  links.insert("rect", "text")
    .attr("x", function(d) {
      return (d.target.x + d.source.x) * width / 200;
    })
    .attr("y", function(d) {
      return (d.target.y + d.source.y) * height / 200;
    })
    .attr("width", function(d) {
      return d.bbox.width + 4;
    })
    .attr("height", function(d) {
      return d.bbox.height;
    })
    .style("fill", "white");

  //NODES
  var nodes = svg.append("g")
    .attr("class", "nodes")
    .selectAll("circle")
    .data(graph.nodes)
    .enter()
    .append("g")
    .attr("transform", function(d) {
      d.x = d.x * width / 100;
      d.y = d.y * height / 100;
      return "translate(" + d.x + "," + d.y + ")";
    });

  nodes.append("circle")
    .attr("r", function(d) {
      return d.radius;
    })
    .attr("class", function(d) {
      return "cerchio";
    })
    .attr("href", "#content")
    .style("fill", function(d) {
      return d.color;
    });

  nodes.append("text")
    .attr("text-anchor", "middle")
    .attr("dy", ".35em")
    .text(function(d) {
      return d.label;
    });

  //SINGLE CLICK BEHAVIOR
  function singleClick(d) {
    var html = $(d.target).attr('href') ? $($(d.target).attr('href'))[0].outerHTML : $($(d.target).prev('circle').attr('href'))[0].outerHTML;
    $.colorbox({
      html: html,
      width: "90%",
      height: "90%"
    });
  }

  //DOUBLE CLICK BEHAVIOR
  function dblclicked(d) {
    if (d3.event.defaultPrevented) return;
    var html = $(d.target).attr('href') ? d.target : $(d.target).prev('circle')[0];

    d3.selectAll("circle").attr("r", 60).style("fill", "#BDD2A6");
    d3.select(html).attr("r", 90).style("fill", "#345830");
  }

  //DRAG BEHAVIOR
  function draggedNode(d) {
    d.x = d3.event.x;
    d.y = d3.event.y;
    //HERE THE STOP PROPAGATION
    d3.event.sourceEvent.stopPropagation(); 

    d3.select(this).attr("transform", function(d) {
      return "translate(" + d.x + "," + d.y + ")";
    });
    links.select("line").filter(function(l) {
      return l.source === d;
    }).attr("x1", d.x).attr("y1", d.y);
    links.select("line").filter(function(l) {
      return l.target === d;
    }).attr("x2", d.x).attr("y2", d.y);
  }

  // //ZOOM BEHAVIOR
  function zoomed() {
    $('.nodes').attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
    $('.link').attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
  }

  //CLICK VS DOUBLE CLICK
  function clickVSDoubleClick() {
    var event = d3.dispatch('click', 'dblclick');

    function cc(selection) {
      var down,
        tolerance = 5,
        last,
        wait = null;
      // euclidean distance
      function dist(a, b) {
        return Math.sqrt(Math.pow(a[0] - b[0], 2), Math.pow(a[1] - b[1], 2));
      }
      selection.on('mousedown', function() {
        down = d3.mouse(document.body);
        last = +new Date();
      });
      selection.on('mouseup', function() {
        if (dist(down, d3.mouse(document.body)) > tolerance) {
          return;
        } else {
          if (wait) {
            window.clearTimeout(wait);
            wait = null;
            event.dblclick(d3.event);
          } else {
            wait = window.setTimeout((function(e) {
              return function() {
                event.click(e);
                wait = null;
              };
            })(d3.event), 300);
          }
        }
      });

    };
    return d3.rebind(cc, event, 'on');
  }

  var drag = d3.behavior.drag().origin(function(d) {
    return d;
  }).on("drag", draggedNode);
  var cc = clickVSDoubleClick();
  var zoom = d3.behavior.zoom().scaleExtent([0.1, 10]).on("zoom", zoomed);
  svg.call(zoom).on("dblclick.zoom", null);
  nodes.call(cc).call(drag);
  cc.on('click', singleClick);
  cc.on('dblclick', dblclicked);




  //  FORCE PART
  var force = d3.layout.force()
    .size([width, height])
    .nodes(graph.nodes)
    .links(graph.links)
    .linkDistance(width / 5)
    .gravity(0)
    .start();

  var animationStep = 100;

  force.on('tick', function() {

    nodes.transition().ease('linear').duration(animationStep)
      .attr("transform", function(d) {
        return "translate(" + d.x + "," + d.y + ")";
      });

    links.select("line").transition().ease('linear').duration(animationStep)
      .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;
      });

    links.select("text").transition().ease('linear').duration(animationStep)
      .attr("x", function(d) {
        return (d.target.x + d.source.x) / 2;
      })
      .attr("y", function(d) {
        return (d.target.y + d.source.y) / 2;
      });

    links.select("rect").transition().ease('linear').duration(animationStep)
      .attr("x", function(d) {
        return ((d.target.x + d.source.x) / 2 - d.bbox.width / 2) - 2;
      })
      .attr("y", function(d) {
        return ((d.target.y + d.source.y) / 2 - d.bbox.height / 2);
      });

    force.stop();
    setTimeout(function() {
      force.start();
    }, animationStep);
  });
  // END FORCE
});


Source: stackoverflow-javascript

Leave a Reply

Your email address will not be published. Required fields are marked *