feat(core): Controller rubberBand option - #10481
Conversation
|
This implementation seems unnecessarily complicated - I especially dislike the fact that the rubberBand setting affects multiple layers of abstraction: gesture (drag, multiTouchDrag), action (pan), and normalization. Should at least push for containing the logic in applyConstraints(). The persistent statefulness in a global singleton also looks suspicious. If the name of this option doesn't contain "maxBounds", it's reasonable to expect it to also apply to all constraints, such as maxZoom/minZoom? |
|
@Pessimistress Thanks for quick review
Done with small exception where required as described above
Removed the global state.
Renamed to |
|
I pushed a follow-up addressing two reproducible interaction issues in
The regression coverage exercises exact and floating-point-inverted fits, undersized content, both axes and diagonals, slow and threshold-speed releases, uninterrupted spring frames, preserved gesture interruption, the complete 300 ms return, and genuine long-distance flings. The controller documentation now distinguishes rebound duration from fling inertia. Validation: |
|
@Pessimistress Final follow-up on the concerns above and the blocking inline review:
The state-layer revision is in Coverage now includes direct state calls, Shift-remapped and multi-touch actions, all edges and diagonals, exact-fit/non-fitting axes, inward release, interruption, native slow/fast inertia, programmatic and keyboard hard bounds, and private-metadata cleanup. All 63 focused tests, lint, build, and website checks pass. The full headless run passes 846 tests; its sole loading-widget failure reproduces on clean Could you take another look when convenient? |
8b04520 to
46d1687
Compare
46d1687 to
62a2df6
Compare
Background
maxBoundskeeps orthographic content inside the viewport, but a hard stop can make a drag or fling feel abrupt. This RFC proposes opt-in, bounds-specific elasticity: panning can briefly overshoot under increasing resistance, then smoothly settle back inside the valid bounds.Existing behavior remains unchanged unless the new option is explicitly enabled.
Proposed API
maxBoundsRubberBand?: boolean; defaultfalse.maxBounds; it does not change minimum or maximum zoom.zoomXandzoomY.Infographic
Video: actual orthographic example
The existing
/examples/orthographic-viewexample enablesmaxBoundsRubberBandalongsidemaxBoundsand can demonstrate horizontal, vertical, and diagonal overscroll with real pointer input.Response to Xiaoji's review
Xiaoji identified the key architectural issue:
A physical pointer event is not the same thing as a semantic action: deck.gl can map ordinary dragging, modifier keys, and two-finger input to the same
panStart/pan/panEndoperations. This revision therefore makes rubber-banding a property of orthographic state, not of a particular input callback.Release belongs to
OrthographicState.panEnd(). The state detects elastic overscroll, computes the exact bounded endpoint, and returns the 300 ms rebound transition. It retains the actual semantic pan target before inherited inertia projects another position, so inward and diagonal releases return directly without snapping or changing axes.All input mappings use inherited controller behavior. The orthographic
_onPanMoveEndoverride and customcontrollerStategetter are gone. Ordinary pointer actions, Shift-remapped panning, and multi-touch panning all reach the same state-owned release through deck.gl's existing dispatch.Constraint behavior lives in
applyConstraints(). Independent viewport axes, progressive resistance, exact-fit and non-fitting bounds, zoom constraints, and transient-frame normalization are handled byOrthographicState. A shared per-axis settled-target calculation prevents a centered non-fitting axis from being mistaken for overscroll.No persistent global bookkeeping. The earlier global
WeakSetis gone. Rebound identity is private state, and a transientSymbolexists only while normalizing a real rebound frame; neither appears in emitted view state or serialization.Ordinary transitions remain native.
OrthographicControlleragain defaults to the standardLinearInterpolator. Only an actually overscrolledpanEnd()selects the private rebound interpolator, so opt-out, unbounded, programmatic, keyboard, and in-bounds transitions retain their existing behavior and configured duration.Interaction state follows the semantic rebound. A one-shot state signal keeps
isPanning: truewhile the 300 ms return is active, then normal transition completion or interruption cleans upisDragging,isPanning, andinTransition. This is consumed generically byController.updateViewport; no input callback needs to know about rubber-banding.Native fling semantics are preserved. The event-specific 0.3 px/ms cutoff was removed because raw pointer velocity is deliberately unavailable to the semantic, zero-argument
panEnd()action. In-bounds releases keep deck.gl's configured inertia; only a view that is actually overscrolled uses the fixed 300 ms rebound.Why the small interpolator remains
The private
LinearInterpolatorsubclass is selected byOrthographicState.panEnd()only for a real rebound. It preserves temporary overscroll through transition-state normalization, returns an exact bounded endpoint, and recognizes a zero-duration gesture update as an interruption. The normal orthographic default remains deck.gl's standard interpolator, and the private subclass and phase symbol are absent from generated public declarations.The RFC remains an opt-in, five-file change: one documented controller option, the orthographic state implementation, focused regression coverage, and the existing website example.
Validation
master(a02590afdc).OrthographicState.panEnd(), Shift-remapped pointer actions, multi-touch release, interruption, active interaction state, diagonal and inward rebounds, exact-fit and non-fitting axes, native slow/fast inertia, animated programmatic updates, and private-metadata cleanup.yarn --frozen-lockfile: passed; no lockfile changes.yarn lint: passed.yarn build: passed, including TypeScript declarations and all bundles.yarn test-website: passed, including the package rebuild, Docusaurus client/server production build, static generation, and final example build.yarn test-headless: 930 passed, 7 skipped, 2 failed across 210 files; all 59 controller tests passed. The CARTO raster-tile failure passed on focused rerun. The remaining WebGPU heatmap timeout reproduces unchanged on the exact currentmastercommit above.maxBoundsRubberBandwithout the private phase symbol, rebound interpolator, or controller action overrides.git diff --check: passed.Regression coverage includes default hard clamping, all four edges, diagonal resistance, independent-axis zoom, exact-fit and undersized content, semantic and remapped pan actions, pointer and touch interruption, fixed-duration spring-back, native configured inertia, programmatic and keyboard constraints, disabled panning, unbounded views, and metadata-free public view state.