From 4fb19ed57638fab7d183338b515a7163b0a4fc42 Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Sun, 26 Apr 2026 12:01:54 +0330 Subject: [PATCH] fix magnets --- src/pages/editor/components/EditorCanvas.tsx | 2 +- src/pages/editor/components/canvas/useSnap.ts | 86 +++++++++++++++---- 2 files changed, 68 insertions(+), 20 deletions(-) diff --git a/src/pages/editor/components/EditorCanvas.tsx b/src/pages/editor/components/EditorCanvas.tsx index 0cff1ef..bedce4f 100644 --- a/src/pages/editor/components/EditorCanvas.tsx +++ b/src/pages/editor/components/EditorCanvas.tsx @@ -199,7 +199,7 @@ const EditorCanvas = ({ catalogSize }: EditorCanvasProps) => { const node = e.target; if (!node || node.name() === "guide-line") return; - const snapped = getSnappedPosition({ x: node.x(), y: node.y() }, guides); + const snapped = getSnappedPosition(node, guides); if (node.x() !== snapped.position.x || node.y() !== snapped.position.y) { node.position(snapped.position); diff --git a/src/pages/editor/components/canvas/useSnap.ts b/src/pages/editor/components/canvas/useSnap.ts index c6d8619..1e1f871 100644 --- a/src/pages/editor/components/canvas/useSnap.ts +++ b/src/pages/editor/components/canvas/useSnap.ts @@ -1,3 +1,5 @@ +import type Konva from "konva"; + export type Guide = { id: string; orientation: "vertical" | "horizontal"; @@ -11,31 +13,77 @@ export type SnappedPositionResult = { activeGuideIds: string[]; }; +type AxisSnapCandidate = { + guideId: string; + delta: number; + distance: number; +}; + +const findBestAxisSnap = ( + guideInfos: Array<{ id: string; position: number }>, + anchors: number[], +): AxisSnapCandidate | null => { + let best: AxisSnapCandidate | null = null; + + guideInfos.forEach((guide) => { + anchors.forEach((anchor) => { + const distance = Math.abs(anchor - guide.position); + if (distance > SNAP_THRESHOLD) { + return; + } + + if (!best || distance < best.distance) { + best = { + guideId: guide.id, + delta: guide.position - anchor, + distance, + }; + } + }); + }); + + return best; +}; + export const getSnappedPosition = ( - pos: { x: number; y: number }, + node: Konva.Node, guides: Guide[], ): SnappedPositionResult => { - let x = pos.x; - let y = pos.y; + const layer = node.getLayer(); + const rect = node.getClientRect({ + skipShadow: true, + ...(layer ? { relativeTo: layer } : {}), + }); + let x = node.x(); + let y = node.y(); const activeGuideIds: string[] = []; - guides.forEach((guide) => { - if ( - guide.orientation === "vertical" && - Math.abs(pos.x - guide.position) <= SNAP_THRESHOLD - ) { - x = guide.position; - activeGuideIds.push(guide.id); - } + const verticalGuides = guides + .filter((guide) => guide.orientation === "vertical") + .map((guide) => ({ id: guide.id, position: guide.position })); + const horizontalGuides = guides + .filter((guide) => guide.orientation === "horizontal") + .map((guide) => ({ id: guide.id, position: guide.position })); - if ( - guide.orientation === "horizontal" && - Math.abs(pos.y - guide.position) <= SNAP_THRESHOLD - ) { - y = guide.position; - activeGuideIds.push(guide.id); - } - }); + const verticalSnap = findBestAxisSnap(verticalGuides, [ + rect.x, + rect.x + rect.width / 2, + rect.x + rect.width, + ]); + if (verticalSnap) { + x += verticalSnap.delta; + activeGuideIds.push(verticalSnap.guideId); + } + + const horizontalSnap = findBestAxisSnap(horizontalGuides, [ + rect.y, + rect.y + rect.height / 2, + rect.y + rect.height, + ]); + if (horizontalSnap) { + y += horizontalSnap.delta; + activeGuideIds.push(horizontalSnap.guideId); + } return { position: { x, y },