Domanda

I am implementing a geometric zoom behaviour as seen in this example

force directed graph

The problem is that if the cursor is on a white spot outside the green overlay rect or any other SVG element (line, circle etc.) the mousewheel event gets intercepted by the browser and scrolls down the page.

I would like to be able to freely zoom independently of where I am on the visualisation.

Here is a simplified jsFiddle recreating the problem.

var width = 300,
    height = 300;

var randomX = d3.random.normal(width / 2, 80),
    randomY = d3.random.normal(height / 2, 80);

var data = d3.range(2000).map(function() {
  return [
    randomX(),
    randomY()
  ];
});

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height)
  .append("g")
    .call(d3.behavior.zoom().scaleExtent([-8, 8]).on("zoom", zoom))
  .append("g");

svg.append("rect")
    .attr("class", "overlay")
    .attr("width", width)
    .attr("height", height);

svg.selectAll("circle")
    .data(data)
  .enter().append("circle")
    .attr("r", 2.5)
    .attr("transform", function(d) { return "translate(" + d + ")"; });

function zoom() {
  svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
È stato utile?

Soluzione

Hope this isn't too late, I missed this question the first time around.

The reason it isn't working under Chrome is because Chrome hasn't yet implemented the standard CSS transform on html elements -- and as strange as it is to understand, the outermost <svg> tag on an SVG element embedded in a webpage is treated as an HTML element for layout purposes.

You have two options:

  1. Use Chrome's custom transform syntax, -webkit-transform in addition to the regular transform syntax:

    http://jsfiddle.net/aW9xC/5/

    Kind of jumpy, since you are transforming the entire SVG and readjusting the page layout accordingly. For reasons I don't understand neither the CSS/webkit transform nor the SVG attribute transform work when applied to the "innerSVG" element.

  2. Replace the nested SVG structure with an SVG <g> group element, which Chrome has no problem transforming:

    http://jsfiddle.net/aW9xC/4/

Altri suggerimenti

Stick a transparent rect in front of everything so the mouse event has something to latch on to. In SVG events are only sent to rendered elements such as rects and not to the general unrendered background.

svg.append("rect")
    .attr("fill", "none")
    .attr("pointer-events", "all")
    .attr("width", "100%")
    .attr("height", "100%");

In order to make this work properly the SVG would have to cover the whole area so to get the same look as your original fiddle you'd want to clip to the original area which can be done either by setting a clipPath or (as I've done in the fiddle) by creating an innser <svg> element which will clip.

var svg = d3.select("body").append("svg")
    .attr("width", "100%")
    .attr("height", "100%")
    .call(d3.behavior.zoom().scaleExtent([-8, 8]).on("zoom", zoom));

svg = svg.append("svg")
    .attr("width", width)
    .attr("height", height)

So altogether it looks like this...

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top