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

Pinch zoom moves image away from screen center #512

Closed
jsblanco opened this issue Sep 21, 2020 · 21 comments · Fixed by #607
Closed

Pinch zoom moves image away from screen center #512

jsblanco opened this issue Sep 21, 2020 · 21 comments · Fixed by #607

Comments

@jsblanco
Copy link

Describe the bug

For some reason, when using my Panzoom app on mobile (any device), when trying to pinch zoom the image moves to the corner right when zooming in, and to the top left when zooming out, instead of zooming in place. Sometimes, when just starting the app, zoom out works properly before moving the image, and then the problem come back.

Zooming in and out with the scroll wheel works as it should, it's only pinch zoom that doesn't.

Your environment

  • Latest version (installed today)
  • Vivaldi browser, Kiwi browser, Chrome, Firefox, all latest versions.

Expected behavior

Zoomed image should stay centered where the user is zooming in/out.

Actual behavior

Zoomed image moves, and zooming in centers the bottom right of the image on the screen, while zooming out centers the top left.

@timmywil
Copy link
Owner

Thanks for opening an issue. I tried the demo on iOS and Android and could not reproduce. You listed desktop browsers, but pinch zooming is mainly for mobile (though some devices have both a mouse and a touch screen). It might be helpful to know which device you are using, even though I'd likely be unable to test on it.

There's not much I can do without being able to reproduce the problem. If you dig deeper and find that a code change in Panzoom fixes it (or that a different test case besides the demo reproduces the problem), please comment here and I'll consider a patch.

@jsblanco
Copy link
Author

jsblanco commented Sep 22, 2020 via email

@rmatec
Copy link

rmatec commented Sep 22, 2020

I am having the same issue. It looks like the focal point is not properly calculated and it is all the way at the bottom.

@timmywil
Copy link
Owner

@jsblanco I see. Panzoom is relying on transform-origin: 50% 50%, which centers the animation on the center of the viewport rather than on the center of the image, which would be lower in this case. It moves the image because of the contain: outside setting to prevent the image from going too far down. I think this can be fixed by calling zoomToPoint or by using zoom with the focal option instead and finding the point in the viewport that is actually the center of the image. Does that make sense?

I could perhaps do something like this by default when contain: outside is set.

@rmatec Are you also using contain: outside?

@rmatec
Copy link

rmatec commented Sep 22, 2020

I am not using the contain option. In my case, it looks like the focal point is at the bottom center. Pinching in or out is done relative to that point.

@timmywil
Copy link
Owner

timmywil commented Sep 22, 2020

@rmatec I think there must be some option in play, as I don't see this behavior in the demos. Is there another option you are setting?

@rmatec
Copy link

rmatec commented Sep 22, 2020

I am using the following to initialise Panzoom.

const panzoomConfig = { minScale: 1, maxScale: 2, step: 0.5, duration: 200 };

It works perfectly well on desktop but for some reason it doesn't on mobile.

@timmywil
Copy link
Owner

Ok, your case sounds like it may be different. Could you reproduce in https://jsbin.com or https://codepen.io and make a new ticket? Here's a template that can be cloned https://jsbin.com/dibofogini/1/edit?html,js,output.

@rmatec
Copy link

rmatec commented Sep 22, 2020

Will do. Thanks.

@jsblanco
Copy link
Author

jsblanco commented Sep 22, 2020 via email

@timmywil
Copy link
Owner

@jsblanco Sorry, it's more complicated than that. The way to do it would be to find the center of the image and translate that to its point in the panzoom parent viewport. You'd then pass that as the focal option, and it should be calculated each time you call zoom. However, let me see if I can work up an example for you. That would fix zoomIn/zoomOut.

That said, this would not change the pinch zooming behavior. Arguably, that is doing the right thing as zooming on certain points bumps up against contain: outside. I'm not sure the fix there except perhaps to not allow the zoom if the image needs to move in order to keep it in the viewport properly.

@timmywil timmywil reopened this Sep 22, 2020
@rmatec
Copy link

rmatec commented Sep 22, 2020

Ok, your case sounds like it may be different. Could you reproduce in https://jsbin.com or https://codepen.io and make a new ticket? Here's a template that can be cloned https://jsbin.com/dibofogini/1/edit?html,js,output.

I've created #513 for my specific case. There is a link to JSBin that shows the issue.

@YanDevDe
Copy link

YanDevDe commented Oct 1, 2020

I'm able to reproduce this issue if I leave "animate: true" on.
This issue doesn't appear at "animate: false" - I've cloned your template @timmywil to reproduce that issue:

https://jsbin.com/pemoveyeri/1/edit?

(I've implemented the wheel event listener to zoom directly - If you scroll fast enough, shifting position will happen.)

@YanDevDe
Copy link

YanDevDe commented Oct 1, 2020

Okay, I moved my issue to #515 because while the issue is same, but it has with different configuration.

@jsblanco
Copy link
Author

jsblanco commented Oct 8, 2020

Hi;

Sorry for taking long to answer. I don't think it has to do with contain="outside"; I just removed it and it still exhibited the same behaviour, although not as intensely. The panzoom div and the image have a set size independent of that of the street, however; could that be part of the issue?

Thank you

@timmywil
Copy link
Owner

@jsblanco The fix should be the same.

@e0
Copy link

e0 commented Oct 14, 2020

Hi, I was going to open a new issue about wheel zoom moving away from center but looked through this one and thought it is related enough. Following are my findings and workaround. Note that I have only tested in browsers on a MacBook using the trackpad.

I notice that for the focal point zooming demo, the center floats towards the upper left when zooming in Safari, Firefox. I get the same behaviour even in Chrome, but only when the web inspector is open.

So I started looking into it and my suspicion is that the mouse wheel event is triggering too often and things are not updating correctly due to the async nature of Panzoom. I thought if I try to throttle the frequency of the zooming it might help and sure enough it seems to "work". You can see it in action here: https://jsbin.com/joliduwulo/1/edit?html,js,output.

The caveat is that the zooming would appear less smooth. In this example I'm limiting it to one zoom max every 20ms but when I was testing with large SVGs, especially in Firefox, I needed to have that set to around 50ms for it to not lose the focal point.

@timmywil please let me know if you want me to open a new issue anyways. This seems like a hack but it solves the problem for me. If you think the solution is worth exploring I would love to help contribute.

@madushandsn
Copy link

Replace move function on panzoom.ts:426

function move(event: PointerEvent) {
if (
!isPanning ||
origX === undefined ||
origY === undefined ||
startClientX === undefined ||
startClientY === undefined
) {
return
}
addPointer(pointers, event)
const current = getMiddle(pointers)
if (pointers.length > 1) {
// Use the distance between the first 2 pointers
// to determine the current scale
// prevent zoom issue in mobile
if (startDistance === 0) {
startDistance = getDistance(pointers);
}
const diff = getDistance(pointers) - startDistance
const toScale = constrainScale((diff * options.step) / 80 + startScale).scale
zoomToPoint(toScale, current)
} else {
// added else condition to prevent mobile zoom focal point error
pan(
origX + (current.clientX - startClientX) / scale,
origY + (current.clientY - startClientY) / scale,
{
animate: false
}
);
}
}

patorpey added a commit to patorpey/panzoom that referenced this issue Apr 13, 2021
@deltavmap
Copy link

Just wondering if the above is still the best temporary fix for this issue?

@timmywil
Copy link
Owner

I suppose that's the best fix. It does mean that you won't be able to pan while pinching anymore, but sometimes the pan calculations can be off because the zoom just before has not fully rendered. I don't see a good way around that aside from doubling or tripling Panzoom's size and duplicating the browser calculations.

I'll go ahead and incorporate the fix above.

@wasam
Copy link

wasam commented Apr 7, 2022

I now seem to have achieved a quite acceptable pan & zoom by changing the move function to

function move(event) {
            if (!isPanning ||
                origX === undefined ||
                origY === undefined ||
                startClientX === undefined ||
                startClientY === undefined) {
                return;
            }
            addPointer(pointers, event);
            if (pointers.length > 2) return;
            const current = getMiddle(pointers);
            if (pointers.length > 1) {
                // A startDistance of 0 means
                // that there weren't 2 pointers
                // handled on start
                if (startDistance === 0) {
                    startDistance = getDistance(pointers);
                }
                // Use the distance between the first 2 pointers
                // to determine the current scale
                const currentDistance = getDistance(pointers);

                // const diff = currentDistance - startDistance;
                // const targetScale = (diff * options.step) / 80 + startScale;

                if (startDistance <= 0) return;
                const factor = currentDistance / startDistance;
                const targetScale = factor * startScale;

                const toScale = constrainScale(targetScale).scale;
                zoomToPoint(toScale, current);
            }
            // else {
                // Panning during pinch zoom can cause issues
                // because the zoom has not always rendered in time
                // for accurate calculations
                // See https://github.com/timmywil/panzoom/issues/512
                const toX = x + (current.clientX - startClientX) / scale;
                const toY = y + (current.clientY - startClientY) / scale;
                pan(toX, toY, {
                    animate: false
                }, event);
                origX = toX;
                origY = toY;
                startClientX = current.clientX;
                startClientY = current.clientY;
            // }
        }

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

Successfully merging a pull request may close this issue.

8 participants