From c8df3b8054763447398a1f0e11c6a5aaac8148e8 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Wed, 9 Sep 2026 19:36:53 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20resize=20anchored=20at=20start=20(no=20j?= =?UTF-8?q?ump)=20=E2=80=94=20absolute=20target=20from=20RESIZE=5FSTART=20?= =?UTF-8?q?anchor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.ts | 48 ++++++++++++++++++++++++++++++++++-------------- src/ui.tsx | 13 +++---------- 2 files changed, 37 insertions(+), 24 deletions(-) diff --git a/src/main.ts b/src/main.ts index 1e4b83e..3825d14 100644 --- a/src/main.ts +++ b/src/main.ts @@ -122,8 +122,15 @@ export default function () { } // Live-ресайз во время drag'а (кастомные ручки по периметру в UI). - // dir + dx/dy (смещение курсора в px). Для right/bottom — только resize. - // Для left/top — resize + сдвиг окна через reposition (иначе окно "уедет" вправо/вниз). + // Якорь: при RESIZE_START фиксируем старт-позицию (canvas) и старт-размер (px). + // При RESIZE_WINDOW_DRAG считаем АБСОЛЮТНУЮ цель от якоря (dx/dy — смещение курсора в px): + // w = startW + (right? +dx : left? -dx : 0) + // h = startH + (bottom? +dy : top? -dy : 0) + // x = startX + (left? dx/zoom : 0) — противоположный край остаётся на месте + // y = startY + (top? dy/zoom : 0) + // Это устраняет "скачки": позиция не зависит от текущего кадра, а размер и позиция + // переведены в одни единицы (px для размера, canvas/zoom для позиции). + let resizeAnchor: { x: number; y: number; w: number; h: number; dir: string } | null = null function getPos () { try { const p = (figma.ui as any).getPosition() @@ -132,24 +139,37 @@ export default function () { return { x: 0, y: 0 } } } - on('RESIZE_WINDOW_DRAG', function (payload: { width: number; height: number; dx: number; dy: number; dir: string }) { - const { width, height, dx, dy, dir } = payload - const size = clampWindowSize({ width, height }) + on('RESIZE_START', function (payload: { dir: string }) { + const p = getPos() + resizeAnchor = { + x: p.x, + y: p.y, + w: window.innerWidth, + h: window.innerHeight, + dir: payload.dir + } + }) + on('RESIZE_WINDOW_DRAG', function (payload: { dx: number; dy: number; dir: string }) { + if (resizeAnchor == null) return + const { dx, dy, dir } = payload + const zoom = figma.viewport.zoom || 1 + let w = resizeAnchor.w + let h = resizeAnchor.h + let nx = resizeAnchor.x + let ny = resizeAnchor.y + if (dir.indexOf('right') !== -1) w = resizeAnchor.w + dx + if (dir.indexOf('left') !== -1) { w = resizeAnchor.w - dx; nx = resizeAnchor.x + dx / zoom } + if (dir.indexOf('bottom') !== -1) h = resizeAnchor.h + dy + if (dir.indexOf('top') !== -1) { h = resizeAnchor.h - dy; ny = resizeAnchor.y + dy / zoom } + const size = clampWindowSize({ width: w, height: h }) figma.ui.resize(size.width, size.height) lastSize = { width: size.width, height: size.height } - if (dir.indexOf('left') !== -1 || dir.indexOf('top') !== -1) { - const zoom = figma.viewport.zoom || 1 - const p = getPos() - let nx = p.x - let ny = p.y - if (dir.indexOf('left') !== -1) nx = p.x - dx / zoom - if (dir.indexOf('top') !== -1) ny = p.y - dy / zoom - figma.ui.reposition(nx, ny) - } + figma.ui.reposition(nx, ny) }) // Ресайз окна плагина (кастомные ручки по периметру -> RESIZE_WINDOW по pointerup) on('RESIZE_WINDOW', function (windowSize: { width: number; height: number }) { + resizeAnchor = null const { width, height } = clampWindowSize(windowSize) lastSize = { width, height } figma.ui.resize(width, height) diff --git a/src/ui.tsx b/src/ui.tsx index 9e34736..1603e39 100644 --- a/src/ui.tsx +++ b/src/ui.tsx @@ -74,6 +74,8 @@ function Plugin () { resizeRef.current.startY = e.clientY resizeRef.current.startW = window.innerWidth resizeRef.current.startH = window.innerHeight + // Сообщаем main зафиксировать якорь (старт-позиция + старт-размер) ДО первого движения. + emit('RESIZE_START', { dir: dir }) } function onPointerMove (e: PointerEvent) { @@ -81,16 +83,7 @@ function Plugin () { const dir = resizeRef.current.dir const dx = e.clientX - resizeRef.current.startX const dy = e.clientY - resizeRef.current.startY - let w = resizeRef.current.startW - let h = resizeRef.current.startH - if (dir.indexOf('right') !== -1) w = resizeRef.current.startW + dx - if (dir.indexOf('left') !== -1) w = resizeRef.current.startW - dx - if (dir.indexOf('bottom') !== -1) h = resizeRef.current.startH + dy - if (dir.indexOf('top') !== -1) h = resizeRef.current.startH - dy - w = Math.max(320, w) - h = Math.max(200, h) - setCurrentWindowSize({ width: w, height: h }) - emit('RESIZE_WINDOW_DRAG', { width: w, height: h, dx: dx, dy: dy, dir: dir }) + emit('RESIZE_WINDOW_DRAG', { dx: dx, dy: dy, dir: dir }) } function onPointerUpResize () {