software-mansion / react-native-gesture-handler

Declarative API exposing platform native touch and gesture system to React Native.
https://docs.swmansion.com/react-native-gesture-handler/
MIT License
6.13k stars 982 forks source link

[web] Add support for two finger pan #3163

Closed m-bert closed 3 weeks ago

m-bert commented 1 month ago

Description

This PR adds support for two finger panning on touchpad on web.

[!WARNING] Two finger gestures may be used as system/browser gestures (for example swipe to go back). This PR doesn't handle these cases.

Implementation

Implementation is based on WheelEvents. This leads to some limitations - whole state flow of Pan has to be managed inside one callback (onWheel).

Ending gesture

Starting gesture is easy, in contrast to ending it. To finish gesture we use timeout - if no wheel event was received since setTimeout was called, we can end gesture.

Mouse vs Touchpad

It is hard to determine whether mouse or touchpad was used to start the gesture. WheelEvent doesn't have any information about the device, therefore we have to use heuristics to check that.

[!NOTE] You cannot start gesture with mouse scroll.

To see if events were generated with mouse, we check whether wheelDeltaY property (which is now deprecated) of event is multiple of 120. In short, this is standard wheel delta for mouse. Here you can find useful links that will tell more about why it works:

[!CAUTION] While this will work most of the times, it is possible that user will somehow generate this specific wheel delta with touchpad and gesture will not be recognized correctly.

Closes #800

Test plan

Tested on new Two finger Pan example

latekvo commented 1 month ago

As far as I see, WheelEvent.wheelDeltaY isn't supported on firefox, would it be possible to resort to other heuristics like having a threshold on WheelEvent.deltaY in combination with WheelEvent.deltaMode for that browser?

Also, it seems like on MacOS with continuous scroll option enabled, this method of detecting the device will not work, since the values will be smoothed out over a longer period of time instead of being divisible by 120.

Also, on Opera browser, the standard value seems to be 80 not 120 (link).

Overall, would it be possible to detect that mouse is not being used instead of checking if it is? The current method seems to be very prone to false positives when it comes to detecting touchpad being used.

m-bert commented 1 month ago

Overall, would it be possible to detect that mouse is not being used instead of checking if it is?

As discussed, I think there's no better way to check that (but I'm open to discussion if you do find a better approach).

would it be possible to resort to other heuristics like having a threshold on WheelEvent.deltaY in combination with WheelEvent.deltaMode for that browser?

Values of deltaY differ between browsers when it comes to mouse. Also deltaMode is the same for touchpad and for mouse.

The current method seems to be very prone to false positives when it comes to detecting touchpad being used.

Activating on mouse wheel is a side effect of this feature that is not affecting main functionality.

m-bert commented 1 month ago

Have you checked if the behavior is the same as on iPad?

I did and indeed it does (at least the example does) 😅