d3 / d3-zoom

Pan and zoom SVG, HTML or Canvas using mouse or touch input.
https://d3js.org/d3-zoom
ISC License
501 stars 144 forks source link

Google Chrome breaks zoom on svg elements #231

Closed alfaredu closed 3 years ago

alfaredu commented 3 years ago

Zoom only works the first time in Zoom with axis in this URL (also it fails in our development): https://www.d3-graph-gallery.com/graph/interactivity_zoom.html

In Edge Chromium works well.

In Google Chrome, the zoom callback function is called only once.

Fil commented 3 years ago

Good news: Chrome has fixed itself!


Can you clarify with a reproducible example? The page you're linking to seems to work for me, but also uses an outdated version of D3 (i.e. v4).

AlessandroCaroti commented 3 years ago

Same problem with D3 V6 using the mouse wheel over a map. And on stack others have the same problem d3 zoom on scroll does not work anymore on Chrome. This is an example http://bl.ocks.org/nnattawat/9689303

alfaredu commented 3 years ago

Edge Chromium works well, it looks an issue with a Google Chrome update.

Fil commented 3 years ago

Thanks for the link. The block example is using d3v3 though.

I'm using Chrome Version 89.0.4389.90 (Build officiel) (x86_64) on macos and can't reproduce the problem. What versions are you running?

Fil commented 3 years ago

Also please clarify if this notebook works https://observablehq.com/@d3/zoom-canvas

alfaredu commented 3 years ago

Thanks for the link. The block example is using d3v3 though.

I'm using Chrome Version 89.0.4389.90 (Build officiel) (x86_64) on macos and can't reproduce the problem. What versions are you running?

Version 89.0.4389.90 (Build oficial) (64 bits) on Windows 10

Also please clarify if this notebook works https://observablehq.com/@d3/zoom-canvas

This notebook works.

mbostock commented 3 years ago

I believe this is fixed in https://github.com/d3/d3-zoom/pull/225 (after Google broke the web). However, it looks like we haven’t published a release yet.

philjones commented 3 years ago

I've attempted to create a notebook based on the @d3/zoom notebook that illustrates (my view of) the problem.

In my case, I had attached the zoom call to the <g> element (similar to the example that OP posted - zoom is attached to svg.append("rect") and had a rectangle underneath that covers the same size as the svg.

https://observablehq.com/@philjones/zoom-svg-chrome-89-0-4389-90-bug

This notebook works in Firefox and the previous version of Chrome (89.0.4389.82) but not in the latest stable (I'm on Windows 89.0.4389.90).

I'm not sure how to tell if this will be addressed by #225 as both the @d3/zoom notebook and my modification have the same number of those warnings.

Fil commented 3 years ago

@philjones in this case the issue is that you're changing the transform of the g on which you've attached the zoom behavior, so it behaves erratically. You should call d3.zoom() on a parent (ie svg), not on g.

philjones commented 3 years ago

@philjones in this case the issue is that you're changing the transform of the g on which you've attached the zoom behavior, so it behaves erratically. You should call d3.zoom() on a parent (ie svg), not on g.

In the other examples mentioned on this page, they are attaching the events to a rect: https://www.d3-graph-gallery.com/graph/interactivity_zoom.html

SVG.append("rect")
      .attr("width", width)
      .attr("height", height)
      .style("fill", "none")
      .style("pointer-events", "all")
      .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
      .call(zoom);

http://bl.ocks.org/nnattawat/9689303

// Add rect cover the zoomed graph and attach zoom event.
var rect = svg.append("svg:rect")
    .attr("class", "pane")
    .attr("width", width)
    .attr("height", height)
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
    .call(zoom);

They should be attaching to the svg instead?

Fil commented 3 years ago

Yes, changing your notebook to svg.call(d3.zoom() ……… works. However it should also work for the rect. So, I've modified your notebook to create a rect, and attached the zoom to the rect. And now I can see that I get the bug on Chrome 89.0.4389.90 too.

https://observablehq.com/d/e6f7b41561b7a83d

On Chrome, if I inspect the rect, I see it has a wheel event listener, and I can "toggle passive", then the zoom works … but in fact it only half-works, since it cannot preventDefault, and the page moves around when I wheel.

I've tried with the new version of d3-zoom that includes #225, but it doesn't solve the problem.

AlessandroCaroti commented 3 years ago

@philjones in this case the issue is that you're changing the transform of the g on which you've attached the zoom behavior, so it behaves erratically. You should call d3.zoom() on a parent (ie svg), not on g.

What if i attach the zoom to one svg inside another svg?

jhnoor commented 3 years ago

This has broken zoom on all our charts, here's an example. Is it really such a bad idea to attach zoom to rects?

Fil commented 3 years ago

What if i attach the zoom to one svg inside another svg?

it doesn't seem to work: https://observablehq.com/d/bb77214ff9883465

Is it really such a bad idea to attach zoom to rects?

You'll have to ask google if they did this intentionally to break the web, or inadvertently.

In the meantime, and barring some other idea, the only thing we can say is that applications that are broken can be fixed by attaching the zoom to the root svg.

MatthewAlner commented 3 years ago

Same issue on the latest stable Chrome [89.0.4389.82], fine on Safari / Firefox / Edge.

We have independent scrolls on the x and y axis. Our charts are stacked so the page would be a pain to scroll the page if the listener was on the whole svg.

Anyone got a link to the bug filed with chrome?

thomassuckow commented 3 years ago

It appears registering even an empty wheel handler on the svg with a non-passive listener causes the listener inside the svg to behave as desired.

Since I use react for creating the svg I register the handler with https://www.npmjs.com/package/react-use-event-listener

Fil commented 3 years ago

Thanks @thomassuckow ! Just adding svg.on("wheel", () => {}); fixes the zoom set on the svg:rect: https://observablehq.com/d/cdadfdf5fd1094fc

But what to make of this in d3-zoom… we could seek the selection's ownerSVGElement and add this automatically, but since it seems to be a browser bug, shouldn't we hope instead that Chrome gets fixed?

MatthewAlner commented 3 years ago

svg.on("wheel", () => {}) worked for me but only after I monkey patched in #225 i'm stuck on D3 5.16 without doing a full migration. Any chance of a 5.16.1 with that change in it, please?

update:

Adding the event listener on the svg manually without going through D3 worked. In my case this is inside and Angular app so I used a ViewChild

@ViewChild(`chartContainer`) private chartContainerElement: ElementRef;

this.chartElement.nativeElement.querySelector(`svg`).addEventListener(`wheel`, () => {}, {passive: false});

Hopefully that helps someone 😄

MatthewAlner commented 3 years ago

Can this be closed now? I just removed my "Fix" and it's working without it again in Chrome 89.0.4389.114