You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix: resolve drag/resize non-interactive, center-snap, and choppiness bugs
Root cause analysis
-------------------
1. Elements become non-interactive after first drag/resize
moveElement() previously wrote to keep the
properties panel in sync. Writing to any @Tracked property triggers a
Glimmer re-render. Re-rendering causes the {{#if @isSelected}} block in
element-renderer.hbs to insert/remove the .tb-handle-* divs, and in some
cases destroys and recreates the element wrapper DOM node entirely. When
the DOM node is recreated, interact.js loses its internal pointer state
and the element becomes permanently unresponsive until the page reloads.
Fix: remove the selectedElement write from moveElement(). The properties
panel will reflect updated values the next time the user clicks the element
(selectElement is called, which sets selectedElement to the same mutated
object). No re-render occurs during drag/resize.
2. Mouse snaps to element center during drag
interact.modifiers.snap() was configured with:
relativePoints: [{ x: 0, y: 0 }]
This tells interact.js to snap the element's top-left corner to the grid,
not the pointer. Because the pointer is typically somewhere in the middle
of the element, interact.js compensates by jumping the element so its
top-left lands on the nearest grid point — which feels like the pointer
is being pulled to the element's origin (center-snap).
Fix: remove the snap modifier entirely. Free movement is smooth and
accurate. Grid snapping can be re-added later using offset:'startCoords'
which snaps relative to where the drag started, not the element origin.
3. Choppy movement
The snap modifier (relativePoints) combined with restrict (parent bounding
box) ran two constraint passes per pointer event, causing visible jank.
Fix: both modifiers removed from draggable. Only restrictSize (minimum
element dimensions) is kept on resizable, as it has no pointer-position
side effects.
4. Zoom read at event time
The previous implementation captured zoom at interactable-creation time
via a closure. If the user changed zoom after an element was placed, the
drag deltas would be scaled by the wrong factor.
Fix: zoom is now read from this.args.zoom inside each event handler via
a getZoom() closure, so it always reflects the current zoom level.
0 commit comments