From 1baa917c9aade1deff9df86672e3c3cd561910aa Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Tue, 2 Jun 2026 10:20:16 -0400 Subject: [PATCH] free transform: keep pointer capture during a rotate drag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit startLayerRotate and startLayerSelectionRotate called render() on every pointermove, which tore down the .freeLayer wrapper that owns the rotation handle. That dropped pointerCapture mid-drag, so after the first move event the gesture "let go" — the handle stopped following the cursor and only a few degrees of rotation registered before the user had to grab again. Fixed both paths the same way the resize handler does: mutate the layer's rotate (and centre, for multi-layer) and apply the change directly to the affected .freeLayer's transform / position styles inside move; only call render() on pointerup so the rest of the UI catches up. The sidebar Rotation field updates live via syncLayerInputs() as before. --- src/index.html | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/index.html b/src/index.html index 80da329..d7d1e38 100644 --- a/src/index.html +++ b/src/index.html @@ -20255,16 +20255,21 @@ const ang = Math.atan2(e.clientY - cyPx, e.clientX - cxPx) * 180 / Math.PI; let next = rot0 + (ang - ang0); if (e.shiftKey) next = Math.round(next / 15) * 15; - // Normalize to (-180, 180] for cleaner numeric display. next = ((next + 540) % 360) - 180; layer.rotate = next; - render(); + // Update the freeLayer's transform in place — calling render() + // here would tear down the handle the user is dragging and + // break pointer capture, so the drag would "let go" after one + // tick. The transform style is what renderLayers writes too. + el.style.transform = `rotate(${next}deg)`; + syncLayerInputs?.(); }; const up = () => { document.body.classList.remove('dragging-layer'); handle.removeEventListener('pointermove', move); handle.removeEventListener('pointerup', up); handle.removeEventListener('pointercancel', up); + if (snapshotted) render(); }; handle.addEventListener('pointermove', move); handle.addEventListener('pointerup', up); @@ -20323,14 +20328,20 @@ s.layer.x = nx - w / 2; s.layer.y = ny - h / 2; s.layer.rotate = ((s.rot + delta + 540) % 360) - 180; + // Update each affected freeLayer in place — render() inside + // the drag tears down the handle and breaks pointer capture. + updateLayerElementBox(s.layer, isDesignerLayer); + const layerEl = document.querySelector(`.freeLayer[data-layer-id="${s.layer.id}"]`); + if (layerEl) layerEl.style.transform = `rotate(${s.layer.rotate}deg)`; } - render(); + syncLayerInputs?.(); }; const up = () => { document.body.classList.remove('dragging-layer'); handle.removeEventListener('pointermove', move); handle.removeEventListener('pointerup', up); handle.removeEventListener('pointercancel', up); + if (snapshotted) render(); }; handle.addEventListener('pointermove', move); handle.addEventListener('pointerup', up);