Skip to content

feat(dataZoom): add per-axis wheel pan/zoom options for 'inside' dataZoom#21588

Open
takaebato wants to merge 1 commit intoapache:masterfrom
takaebato:feat/datazoom-xy-wheel
Open

feat(dataZoom): add per-axis wheel pan/zoom options for 'inside' dataZoom#21588
takaebato wants to merge 1 commit intoapache:masterfrom
takaebato:feat/datazoom-xy-wheel

Conversation

@takaebato
Copy link
Copy Markdown

@takaebato takaebato commented Apr 19, 2026

Brief Information

This pull request is in the type of:

  • bug fixing
  • new feature
  • others

What does this PR do?

Adds moveOnMouseWheelAxis and zoomOnMouseWheelAxis options that restrict an inside dataZoom to a single wheel axis, so horizontal and vertical wheel components can drive different dataZooms independently.

Fixed issues

Details

Before: What was the problem?

  1. No per-axis wheel routing. zrender's polyfill folds deltaX and deltaY into a single wheelDelta
    scalar, so the horizontal and vertical components of a wheel event can't be directed to different dataZooms
    separately.
  2. No axis restriction. When two inside dataZooms share a coord sys (one on each cartesian axis), any wheel direction pans / zooms both — there was no way to split them.

After: How does it behave after the fixing?

  1. RoamController's 'zoom' and 'scrollMove' events now carry per-axis fields (scaleX / scaleY,
    scrollDeltaX / scrollDeltaY) alongside the existing scale / scrollDelta scalars. Per-axis values are
    polyfilled from the native WheelEvent when deltaX / deltaY are available; pre-2013 IE falls back to
    caller-supplied defaults. Touch pinch mirrors the scalar into both per-axis fields since it has no distinct
    direction.
  2. Two new options on dataZoom.inside:
  • moveOnMouseWheelAxis?: 'horizontal' | 'vertical' — pin the pan to a single wheel axis.
  • zoomOnMouseWheelAxis?: 'horizontal' | 'vertical' — pin the zoom to a single wheel axis.
  1. With both options unset, existing behavior is preserved.

Document Info

One of the following should be checked.

  • This PR doesn't relate to document changes
  • The document should be updated later
  • The document changes have been made in apache/echarts-doc#xxx

Misc

Security Checking

  • This PR uses security-sensitive Web APIs.

ZRender Changes

  • This PR depends on ZRender changes (ecomfe/zrender#xxx).

The per-axis values are read directly from the native WheelEvent (via e.event.deltaX / .deltaY, with a fallback to
the legacy WebKit wheelDeltaX / wheelDeltaY), so zrender's scalar wheelDelta API stays untouched and no companion zrender PR is needed.

Related test cases or examples to use the new APIs

test/dataZoom-inside-wheel-axis.html (added in this PR) — 11 manual panels covering the default fallback,
moveOnMouseWheelAxis / zoomOnMouseWheelAxis with various axis configs, zoom / pan composition, shift-based
horizontal scroll, and polar coordinate systems.

Merging options

  • Please squash the commits into a single one when merging.

Other information

Relation to #18365. Same underlying problem (zrender's wheelDelta collapses both wheel axes into a single scalar, so horizontal and vertical inputs can't be routed separately), different API. Rather than extending moveOnMouseWheel /
zoomOnMouseWheel with composite values like 'x+shift' / 'y+none', this PR splits the concern in two:

  • moveOnMouseWheelAxis / zoomOnMouseWheelAxiswhich wheel axis drives the dataZoom.
  • moveOnMouseWheel / zoomOnMouseWheelunder which modifier state the action fires.

Keeping the two orthogonal means axis restriction composes cleanly with any existing modifier mode, without a
combinatorial explosion of composite strings. The 'none' modifier value also mentioned in that issue is out of
scope here and can follow separately.

Touch pinch. Axis restriction applies only to wheel-driven pan / zoom. Touch pinch keeps zooming on every dataZoom
regardless of the axis restriction setting — a touch pinch is a 2D gesture with no distinct horizontal / vertical
component.

Future work. This is independent of the axis API introduced in this PR, but a follow-up is planned to tackle
the magnitude side. The FIXME in RoamController sits on a 3-tier heuristic that ignores deltaMode, still carries the IE-era / 120 division, and feels a bit fast on some setups. The plan is to derive the magnitude from the native WheelEvent directly, retire the FIXME, and add a wheelSensitivity option.

@echarts-bot echarts-bot Bot added PR: awaiting doc Document changes is required for this PR. PR: awaiting review labels Apr 19, 2026
@echarts-bot
Copy link
Copy Markdown

echarts-bot Bot commented Apr 19, 2026

Thanks for your contribution!
The community will review it ASAP. In the meanwhile, please checkout the coding standard and Wiki about How to make a pull request.

Please DO NOT commit the files in dist, i18n, and ssr/client/dist folders in a non-release pull request. These folders are for release use only.

Document changes are required in this PR. Please also make a PR to apache/echarts-doc for document changes and update the issue id in the PR description. When the doc PR is merged, the maintainers will remove the PR: awaiting doc label.

Copy link
Copy Markdown
Contributor

@Justin-ZS Justin-ZS left a comment

Choose a reason for hiding this comment

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

Thanks, the API split between modifier gating and wheel-axis gating is clean, and the manual coverage is thoughtful. I found one blocking interaction issue, though: when the wheel direction does not match moveOnMouseWheelAxis / zoomOnMouseWheelAxis, the dataZoom becomes a no-op but the wheel event is still consumed in RoamController, so ordinary page/container scrolling is blocked. I think that axis filtering needs to happen before eventTool.stop() so non-matching wheel input can fall through.

this._checkTriggerMoveZoom(this, 'zoom', 'zoomOnMouseWheel', e, {
scale: scale, originX: originX, originY: originY, isAvailableBehavior: null
scale: scale,
scaleX: axisZoomScale(nativeEvent, 'horizontal', 1),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

With moveOnMouseWheelAxis: 'horizontal' / zoomOnMouseWheelAxis: 'horizontal', a plain vertical wheel becomes a no-op in InsideZoomView (effectiveDelta === 0 / effectiveScale === 1). But the event is still consumed later in _checkTriggerMoveZoom(), so the page/container can no longer scroll. That turns an axis mismatch from "ignored" into "blocked". Could we move the axis check earlier so non-matching wheel directions fall through like the existing modifier-gated cases do?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@Justin-ZS

Thanks! I hadn't considered that the event is consumed even when the axis doesn't match. Let me think through the right place to hoist the check and I'll follow up.

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

Labels

PR: awaiting doc Document changes is required for this PR. PR: awaiting review size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants