Skip to content

Add 'reverse' option to wheel gesture#660

Open
jeroenkroese wants to merge 1 commit into
pmndrs:mainfrom
jeroenkroese:main
Open

Add 'reverse' option to wheel gesture#660
jeroenkroese wants to merge 1 commit into
pmndrs:mainfrom
jeroenkroese:main

Conversation

@jeroenkroese

Copy link
Copy Markdown

This PR adds a reverse option to the wheel gesture, which inverts the direction of wheel scrolling. A few years ago, a similar PR #171 has been closed with the reason that the inversion could easily be done in userland.

In my scenario, I'm using multiple gestures (drag, pinch-to-zoom, and wheel) to navigate around a canvas element, and I'm using the from and bounds properties to limit the canvas movement. This led to some complications when I tried to invert the direction of the wheel scroll. Without having the reverse option, it would require a complex bounds calculation which would be the inverse of the bounds which I'm already calculating for the drag gesture. This was my first approach, however I ran into some weird behaviour which is was not able to easily fix.

The reverse option that is introduced by this PR allows developers in the invert the wheel gesture, while allowing to use the same bounds calculation that is used by the drag gesture. This will make it easier for developers that wish to implement drag and wheel gesture behaviour similar to something like excalidraw.com.

@changeset-bot

changeset-bot Bot commented Apr 2, 2024

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 7f07adb

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@codesandbox-ci

codesandbox-ci Bot commented Apr 2, 2024

Copy link
Copy Markdown

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 7f07adb:

Sandbox Source
gesture-drag Configuration
gesture-drag-target Configuration
gesture-nested Configuration
gesture-drag-vanilla Configuration
gesture-move Configuration
gesture-pinch Configuration
gesture-multiple-pinch Configuration
gesture-three Configuration
gesture-card-zoom Configuration
gesture-viewpager Configuration

@jeroenkroese

Copy link
Copy Markdown
Author

@dbismut could you please take a look?

@dbismut

dbismut commented May 4, 2024

Copy link
Copy Markdown
Collaborator

Hi @jeroenkroese thanks for the PR. Unfortunately I don't have a lot of time to dedicate to OSS atm. What's the benefit of this vs using the transform option beyond simplicity?

https://use-gesture.netlify.app/docs/options/#transform

@jeroenkroese

Copy link
Copy Markdown
Author

Hi @dbismut, I'm sorry for the late reply to your swift response. I got distracted.

I've created a simple reproduction of what I'm trying to achieve. Unfortunately, I could not get it working using the transform option. It seems that the wheel gesture doesn't output anything when the transform option has been configured.

https://codesandbox.io/p/sandbox/use-gesture-zoom-reverse-23npzv?file=%2Fsrc%2FApp.js

@jeroenkroese

Copy link
Copy Markdown
Author

Hi @dbismut, I just noticed that I'd left the codesandbox on private. It should be public now.

Do you think this behaviour should be accomplishable by using the transform option? In that case I think there might be a bug, as I could not get it working. I would appreciate if you could take a quick look.

https://codesandbox.io/p/sandbox/use-gesture-zoom-reverse-23npzv

@travisbreaks travisbreaks left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementation is clean — config resolver, docs update, and a live example with a toggle checkbox. The wheel engine change is minimal (sign flip on delta).

Worth noting: PR #171 proposed something similar and was closed with the reasoning that it's achievable in userland by negating the offset. Has the maintainer stance on this changed, or is there a case where userland negation doesn't cover it (e.g., interaction with bounds or rubberband)?

The docs example is a nice touch — interactive demo makes the behavior immediately clear.

@travisbreaks travisbreaks left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work, @jeroenkroese. Circling back with a deeper look at the implementation.

Prior art acknowledgment: This picks up where #171 left off. The key difference — and what makes this PR more compelling — is the concrete use case around bounds interaction. In #171, @dbismut suggested userland negation (offset * -1), but as both @JJK801 and you've demonstrated, that breaks down when bounds and from are in play because the engine's internal movement tracking and clamping logic operates on the pre-inversion values. Negating after the fact means bounds clip in the wrong direction. That's the real justification for handling this at the engine level.

Implementation review:

The core change in WheelEngine.ts is exactly right — V.subTo vs V.addTo on _movement before bounds clamping runs. This ensures clampStateInternalMovementToBounds operates on the correctly-signed movement, which is the whole point. Clean and minimal.

A few observations:

  1. _delta sign: The delta itself isn't negated — only the accumulation direction flips. This means state.delta in the handler callback will still report the raw wheel direction while movement/offset are reversed. That's probably the right call (preserves raw event info), but it's worth documenting explicitly since someone debugging might expect delta and movement to agree in sign.

  2. Dynamic toggling: The docs example shows a checkbox toggling reverse at runtime. Since the config resolver runs on each gesture start, toggling mid-gesture could produce a discontinuity in offset (the accumulated movement suddenly flips direction). The test only covers a static config. Might be worth either (a) adding a test for mid-session toggle, or (b) noting in docs that reverse is best set before gesture begins.

  3. Changeset missing: The changeset bot flagged this. You'll need one for the version bump — this would be a patch on @use-gesture/core.

  4. Scroll engine parity: Any reason this is wheel-only and not also available on ScrollEngine? The same bounds interaction issue could apply there. Not a blocker, just curious about scope.

Overall this is a well-scoped addition. The type updates are thorough (WheelConfig, UserWheelConfig, InternalConfig), docs include an interactive example, and the test validates the sign arithmetic. The +76/-6 ratio is healthy for a feature that touches config resolution, engine logic, types, docs, and tests.

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 this pull request may close these issues.

3 participants