Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First zoom doesn't move scale to within scaleExtent #269

Open
vincerubinetti opened this issue Aug 17, 2023 · 2 comments
Open

First zoom doesn't move scale to within scaleExtent #269

vincerubinetti opened this issue Aug 17, 2023 · 2 comments

Comments

@vincerubinetti
Copy link

I'm not sure this would be considered a bug, but it certainly caught me off guard and took a lot of time to track down. Panning/dragging works fine, the problem is only with zoom. The problem seems to happen with mouse wheel or pinch zoom.

Here's my (relevant) code for a d3 map visualization:

const limit = 10; // in my real situation, this isn't a constant but determined from a map projection fit to the containing client bbox

const zoom = d3
  .zoom<SVGSVGElement, unknown>()
  .scaleExtent([limit, limit * 10])
  .on("zoom", (event) => updateMap(event));

svg.call(zoom);

svg
  .on("wheel", (event) => event.preventDefault())
  .on("dblclick.zoom", () => {
    zoom.transform(svg, d3.zoomIdentity);
    resetProjection();
    updateMap();
  });

What happened:

While testing on a touch device, I saw that the first pinch zoom on the map did not zoom in, it only panned. On inspection, this is because the event.transform.k being emitted was constant (limit), and not increasing as I pulled my fingers apart. However, after zooming or panning once, the next zoom would work just fine (k would increase).

I eventually figured out that d3-zoom's "internal k" was increasing (as opposed to the emitted transform.k, clamped to scaleExtent), but since it starts off at 1 by default, and limit was a lot higher than that, I simply needed to keep pulling my fingers apart -- like the entire width of my screen -- until the emitted transform.k started increasing.

So I think the scale isn't truly (internally) getting clamped to the scaleExtent until after the first zoom (or pan) completes. I'd expect the transform to be instantiated to values within the scaleExtent, translateExtent, etc.

The solution to all this, for me, is to manually set the scale to the lower limit, right after initializing zoom and also on the double click reset: zoom.scaleTo(svg, limit);

@Fil
Copy link
Member

Fil commented Aug 17, 2023

Is this maybe linked to note 8 of the README (introduced here)?

@vincerubinetti
Copy link
Author

No, that is about ignoring events when trying to go beyond the scaleExtent while currently within it, i.e. trying to zoom in above the upper scale limit, or zoom out below the lower scale limit. This is about starting below the lower scale limit and trying to go up to it.

Technically the k being emitted is clamped to the lower scale limit, but whatever d3-zoom is doing internally to track the progression of zoom isn't working quite right. On every tick of zooming in (one mouse wheel notch, or one touchmove event for pinch zoom), the scale should first be brought to within the scaleExtent, then increased/decreased from there based on the relative "delta k" since the last tick.

I'll try to decipher the code and find the source of the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants