d3 / d3-zoom

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

Emit zoom end event 150 ms after last wheel event which had an effect #158

Closed Herst closed 5 years ago

Herst commented 5 years ago

Currently a zoom end event is emitted 150 ms after the last wheel event. How about changing to 150 ms after the last wheel event which made a difference, so that wheel events which were ignored because the scale extent has been reached don't count towards the timeout?

This would be consistent with the current behaviour of the start event (because there is no start even if "already at the corresponding limit of the scale extent.")

In my specific use case I have non-linear scaling for certain elements in place in order to e.g. make important lines and symbols still visible at certain zoom levels (think of hinting in typography) but for this I do a costly redrawing of a lot of stuff which is why I only do it at the end of a zoom operation and during the zoom I am only relying on transform="scale(...)". Now with user which are wild with their scroll wheels (or equivalent) and continue scrolling even after the limit has been reached, you get the end event much later and so they see the results of the smart scaling later.

mbostock commented 5 years ago

Sounds reasonable!

mbostock commented 5 years ago

Fixed in f4c6c792a26a6293a3e43911f2117e28852329c2.

mbostock commented 5 years ago

FYI, I released this in 1.7.4, but then reverted it in 1.8.1. The problem with this change is that if you zooming by wheeling on a scrollable page, it will start scrolling as soon as you hit the limit of the scale extent. What we want instead is that these wheel events are considered part of the same wheeling gesture and thus continue to prevent scrolling, even though the transform is no longer changing. This way you can more predictably control whether the wheel-based gesture will trigger zooming or scrolling.

Herst commented 5 years ago

Ah, so the wheel events are only defaultPrevented while a wheeling gesture is happening? I thought for some reason that they always are (at least while the element in question is selected).

Anyway, fair enough, I see how this could be a problem. In my own applications there are no scrollbars (unless the viewport height is very small) this is why this issue didn't show up for me.

BTW, I just remembered that with Adobe Flash the behaviour apparently was that all wheeling lost its ability to scroll the page (or parent element) while the mouse pointer was hovering above the element in question. I understand why they would do it but it also was a pain in the posterior to suddenly not be able to scroll a page anymore if during scrolling a page such an element happened to sneak below the mouse pointer.

Herst commented 5 years ago

Oh and I also just realised that browser vendors have to deal with a similar issue in terms of when to allow scrolling an element with overflow: scroll; on a page which is scrollable. On a quick glance some browser vendors require the element to have focus to be scrollable, while others apparently require that the mouse hovers over it for a little bit. (You can test it out at https://developer.mozilla.org/en-US/docs/Web/CSS/overflow).

The browsers also differ on when they dispatch the wheel event when the scrolling limits have been reached, apparently some do it right after reaching the limits, while others require a new gesture to be started.