fix: use absolute mouse coordinates to eliminate resize drift#270
fix: use absolute mouse coordinates to eliminate resize drift#270kajtzu wants to merge 2 commits into
Conversation
The previous delta-accumulation approach (including the lastSize fix from react-grid-layout#255 / 3.2.0) still loses movement when React can't re-render between consecutive mousemove events. On heavy dashboards with 12+ widgets (maps, topology views, etc.), renders take 30-50ms, so 3-8 mouse events pile up per frame. Each event computed width as baseWidth + deltaX, but baseWidth (from lastSize or props) was stale for all events after the first in a batch. This commit replaces the incremental delta approach entirely: onResizeStart: record initial widget size + mouse position onResize: width = startWidth + (mouseX - startMouseX) No accumulation, no stale props, no feedback loops. The calculation only depends on two things that are always correct: the initial state (captured once, never changes) and the current mouse position (fresh from the DOM event). Also removes the per-event getBoundingClientRect call that forced a synchronous reflow (40-50ms on complex layouts). It was only needed for north/west handle position correction, which is unnecessary with absolute coordinates. Refs: react-grid-layout#237, react-grid-layout#255
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughResizable now records the starting pointer position and size at resize start, then computes dimensions from absolute pointer displacement. The resize callback fallback and no-op suppression logic were updated to use the new tracking state and reference sizes. ChangesResizable absolute-coordinate resize refactor
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant DraggableCore
participant resizeHandler
participant ResizableState
User->>DraggableCore: onResizeStart drag
DraggableCore->>resizeHandler: onResizeStart(mouseX, mouseY)
resizeHandler->>ResizableState: store startSize, startMouseX, startMouseY
User->>DraggableCore: drag move
DraggableCore->>resizeHandler: onResize(mouseX, mouseY)
resizeHandler->>ResizableState: compute width/height from startSize + displacement
resizeHandler->>ResizableState: compare against reference size for dimensionsChanged
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@lib/Resizable.tsx`:
- Around line 152-155: The resize dedup logic in Resizable should compare the
current width/height against the previous callback size instead of the drag
origin stored in startSize, since startSize only detects movement from the
initial resize and allows repeated identical onResize calls when dimensions are
clamped or unchanged between events. Update the change check in Resizable.tsx to
use the prior lastSize values available in the resize handler before they are
refreshed, so onResize only fires when the per-event size actually changes.
- Around line 116-134: Keep the position correction logic for north/west resize
handles in Resizable.tsx, since DraggableCore x/y are offsetParent-relative and
the origin can move during resizing. Update the resize calculation in the
handle-delta path so the start-position correction is preserved for axisH ===
'w' and axisV === 'n' instead of relying only on absolute displacement. If you
change the behavior, add or adjust a regression test around Resizable /
DraggableCore with a moving offsetParent to verify n/w resizing stays anchored
correctly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
Comparing against startSize caused redundant onResize callbacks when the size was clamped at min/max constraints — every event still differed from the start even though the actual size wasn't changing between events. Compare against lastSize (the previous callback value) to properly suppress no-op events.
Problem
On dashboards with many heavy widgets (maps, topology views, charts), resizing drifts away from the cursor. The handle moves 3-5x slower than the mouse, making it nearly impossible to resize to full width. This is the same core issue reported in #237 and partially addressed in #255.
The
lastSizefix from #255 helped but didn't fully solve it. When React can't re-render between consecutive mousemove events (common when renders take 30-50ms due to complex widget trees), multiple events still share a stale base:The interaction between react-resizable's
lastSizetracking and react-grid-layout'sresizePositionReffeedback loop creates a situation where the accumulated size oscillates or drifts backwards despite steady mouse movement.Fix
Replace the incremental delta approach entirely with absolute mouse displacement:
This has zero accumulation state. The result depends only on:
No
lastSize, no staleprops.width, no feedback loops. Mathematically equivalent to a perfect sum of all deltas, but immune to any inter-render timing issues.Additional change
Removed the per-event
getBoundingClientRect()call. It was forcing a synchronous reflow on every mousemove (measured at 40-50ms on our 12-widget dashboard). The handle position correction it provided was only relevant for north/west handles where the element moves during resize — with absolute coordinates this correction is unnecessary for any axis since DraggableCore'sx/yare page-relative.Testing
Tested on a dashboard with 12 widgets (4 map widgets, 4 topology widgets, timeseries, issue manager). Resize now tracks the mouse 1:1 instead of the previous ~30% tracking ratio. Works correctly for all handle axes (se, sw, ne, nw, n, s, e, w).
Summary by CodeRabbit