fix(studio): canvas zoom improvements — zoom to cursor, reset button, border fix

- Zoom anchors to cursor position instead of always zooming toward center.
  The resolvePreviewWheelZoom function now accepts cursorX/cursorY (offset
  from viewport center) and uses the standard zoom-to-point formula to
  adjust pan so the content point under the cursor stays fixed.

- Add visible "Reset" button (bottom-right) showing current zoom % when
  not at fit zoom. Driven by settledZoom state that updates after the
  200ms settle debounce, so no re-renders during active zoom gestures.

- Fix border-expands-inward bug: scaleIframeToFit in the player now uses
  offsetWidth/offsetHeight instead of getBoundingClientRect. The latter
  returns values inflated by ancestor CSS zoom, causing double-scaling
  that made the iframe appear smaller than its container.

- Fix zoom HUD appearing during pan: split applyZoom (shows HUD) from
  applyPan (silent) so trackpad/middle-mouse panning no longer flashes
  the zoom percentage overlay.

- Fix stale closure performance regression: replace stageSize in effect
  dependency arrays with stageSizeRef pattern. The old deps caused wheel
  and pointer handlers to re-register on every viewport resize.

- Widen pan clamp range (Math.abs instead of Math.max(0,...)) so content
  can float within the viewport when zoomed below fit — required for
  zoom-to-cursor to work correctly at any zoom level.

Closes #900
This commit is contained in:
Miguel Ángel
2026-05-18 13:24:17 -04:00
parent 8163f38077
commit 0150a0a1b4
5 changed files with 161 additions and 50 deletions
+4 -3
View File
@@ -64,9 +64,10 @@ export function scaleIframeToFit(
compositionWidth: number,
compositionHeight: number,
): void {
const rect = playerElement.getBoundingClientRect();
if (rect.width === 0 || rect.height === 0) return;
const scale = Math.min(rect.width / compositionWidth, rect.height / compositionHeight);
const w = playerElement.offsetWidth;
const h = playerElement.offsetHeight;
if (w === 0 || h === 0) return;
const scale = Math.min(w / compositionWidth, h / compositionHeight);
iframe.style.width = `${compositionWidth}px`;
iframe.style.height = `${compositionHeight}px`;
iframe.style.transform = `translate(-50%, -50%) scale(${scale})`;