0

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 + ")");
}
Tibor Udvari
  • 2,932
  • 3
  • 23
  • 39
  • please make your tried code in fiddle..? – Manoj Jan 07 '14 at 09:14
  • In your zoom handler do `d3.event.preventDefault();` and `d3.event.stopPropagation();`. – Lars Kotthoff Jan 07 '14 at 09:21
  • @LarsKotthoff I tried what you suggested, but the problem does not seems to be that the zoom callback is not getting called when we are in the white area; since the zoom handler does not get called the code has no effect. It was logical though. – Tibor Udvari Jan 07 '14 at 10:12
  • @Manoj created a simple jsFiddle recreating the problem essentially (my code had a lot of things going on in it :) ) – Tibor Udvari Jan 07 '14 at 10:13

2 Answers2

4

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/

AmeliaBR
  • 27,344
  • 6
  • 86
  • 119
1

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...

Robert Longson
  • 118,664
  • 26
  • 252
  • 242
  • It sure does for me. I'm using Firefox. – Robert Longson Jan 07 '14 at 11:19
  • I just checked under Firefox and it works; does not work with Chrome though. Would there be no other way other than using this clipping method? The image I posted in my question is the actual scenario I want to use this for, this method would imply losing anything outside the clipPath or inner SVG element. Would there be no other way to achieve this? I do not understand why d3 doesn't get the zoom callback when we are just on the SVG canvas. – Tibor Udvari Jan 07 '14 at 11:39
  • It does not translate or scale. It is correctly clipped, but I can't interact with it. (No zooming, nor dragging) – Tibor Udvari Jan 07 '14 at 13:14
  • Is my new version any better? – Robert Longson Jan 07 '14 at 13:19
  • Nop, still motionless under Chrome. – Tibor Udvari Jan 07 '14 at 13:28
  • @TiborUdvari and Robert Longson; the Chrome-specific problems relate to the fact that a transform on an SVG element uses the CSS/HTML transform process, which Chrome still requires a "-webkit-" prefix for; see a longer discussion as a separate answer to this question. – AmeliaBR Jan 13 '14 at 03:18