use-gesture
use-gesture copied to clipboard
useDrag is stuck on iOS when navigating between pages
Describe the bug
When using the swipe gestures to go backwards/forwards in your browser history it is possible to break the useDrag functionality. This occurs when you initiate the gesture on an element which is bound to useDrag.
- open https://trbo.be/bugs/use-gesture/ on an iOS device
- navigate to other webpage
- go back
- swipe to go forward with your finger on the blue block.
- when you come back to the demo again, the dragging is stuck.
It happens because iOS doesn't refresh the page when going back, but rather uses a cached version.
The workaround I use in our codebase now is to listen on the visibilitychange event and call the cancel function provided by the gesture state.
Sandbox or Video
- demo: https://trbo.be/bugs/use-gesture/
- github: https://github.com/WartClaes/use-gesture-drag-issue
(I used the demo from the website to reproduce the issue, but it is unrelated to the usage of @react-spring/web)
Information:
- React Use Gesture version: 9.1.3 (but also on older versions)
- Device: tested on iPhone Xs and iPhone 12
- OS: iOS 14, iOS 15
- Browser: Safari
Checklist:
- [x] I've read the documentation.
- [x] If this is an issue with drag, I've tried setting
touch-action: noneto the draggable element.
It seems this package is not handling the lostpointercapture events? Might this be the reason?
https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/lostpointercapture_event
Can you try upgrading to @use-gesture/react which is in fact v10?
I updated to @use-gesture/react and the issue is still there.
Both repo and demo are updated
Hi, indeed I can reproduce the issue on the docs website. Thanks for bringing that up.
I'm not exactly sure how to fix this yet: the issue is not so much about pointercancel (which is handled by the lib) or lostpointercapture, as those events don't seem to fire when starting the swipe forward or swipe back (which IMO is what we need).
I believe there might be a workaround with using touch events and preventing default ({ pointer: { touch: true }, preventDefault: true }) that should completely disable the swipe gesture in Safari.
If you have any hints at how I could solve this that would be great!
I had an issue on Android where pointercancel was being fired when I didn't expect it to, and seemed to resolve it by just using { pointer: { touch: true }.
@jzombie very likely you didn't properly set touch-action on your draggable element as mentioned in the docs.
I did, actually. Noticed it on my Android device, then used BrowserStack to do some testing with as well, and noticed it happening on some devices, but not others, using the same Chrome version. It might be related to some other specifics of my implementation, though, but all I know is using the touch events resolved it, and it is consistent on iOS as well.
Just very happy the touch events worked out.
I came across this reddit thread: https://www.reddit.com/r/learnjavascript/comments/wyzit6/detect_ios_safari_backforward_gesture_with/
And the Redditor u/FabianDR makes an interesting observation that ontouchend doesn't fire when navigating forwards - possibly leaving the gesture in a broken state.
Edit: Really odd iOS behavior but if you fully navigate away on a drag forwards:
pointerupandtouchendnever fire
If you backout of a forward navigation halfway through
pointerupandtouchendare skipped- But on next
pointerdownjust atouchendis fired beforehand (missing thepointerup)