From 0badbef52cc77bc0ac937bd8a0963dd6bb900aa7 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 9 Jul 2025 16:02:55 +0900 Subject: [PATCH] =?UTF-8?q?=EC=A4=8C=20=ED=99=95=EB=8C=80,=20=EC=B6=95?= =?UTF-8?q?=EC=86=8C=20=EC=8B=9C=20=EC=98=A4=EB=B8=8C=EC=A0=9D=ED=8A=B8=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D=20=EC=95=88=EB=90=98=EB=8A=94=20=ED=98=84?= =?UTF-8?q?=EC=83=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/fabric/QLine.js | 64 +++++++++++++++++++++++-------- src/components/fabric/QPolygon.js | 37 +++++++++++++++++- src/hooks/useEvent.js | 5 ++- 3 files changed, 87 insertions(+), 19 deletions(-) diff --git a/src/components/fabric/QLine.js b/src/components/fabric/QLine.js index ea316903..af9dd7a4 100644 --- a/src/components/fabric/QLine.js +++ b/src/components/fabric/QLine.js @@ -183,29 +183,64 @@ export const QLine = fabric.util.createClass(fabric.Line, { return this }, - containsPoint: function(point) { + setCoords: function () { + // 부모 클래스의 setCoords 호출 + this.callSuper('setCoords') + + // QLine의 경우 추가 처리 - 항상 강제로 재계산 + if (this.canvas) { + // 모든 좌표 관련 캐시 초기화 + delete this.oCoords + delete this.aCoords + delete this.__corner + + // 다시 부모 setCoords 호출 + this.callSuper('setCoords') + + // 한 번 더 강제로 bounding rect 재계산 + this._clearCache && this._clearCache() + } + }, + + containsPoint: function (point) { + // 먼저 좌표 업데이트 + this.setCoords() + + // 캔버스 줌과 viewport transform 고려한 좌표 변환 + let localPoint = point + if (this.canvas) { + const vpt = this.canvas.viewportTransform + if (vpt) { + // viewport transform 역변환 + const inverted = fabric.util.invertTransform(vpt) + localPoint = fabric.util.transformPoint(point, inverted) + } + } + + // 기본 boundingRect 사용하되 줌을 고려하여 선택 영역 조정 const boundingRect = this.getBoundingRect(true) // 선의 방향 판단 const dx = Math.abs(this.x2 - this.x1) const dy = Math.abs(this.y2 - this.y1) - const isVertical = dx < dy && dx < 10 // 세로선 (가로 변화가 작음) - const isDiagonal = dx > 10 && dy > 10 // 대각선 (가로, 세로 모두 변화) + const isVertical = dx < dy && dx < 10 + const isDiagonal = dx > 10 && dy > 10 + + // 줌 레벨에 따른 선택 영역 조정 + const zoom = this.canvas ? this.canvas.getZoom() : 1 + const baseMultiplier = 1 // 줌이 클수록 선택 영역을 줄임 let reducedWidth, reducedHeight if (isDiagonal) { - // 대각선의 경우: 1/2 크기 - reducedWidth = boundingRect.width / 2 - reducedHeight = boundingRect.height / 2 + reducedWidth = Math.max(boundingRect.width / 2, 10 * baseMultiplier) + reducedHeight = Math.max(boundingRect.height / 2, 10 * baseMultiplier) } else if (isVertical) { - // 세로선의 경우: 가로 선택범위 2배 - reducedWidth = boundingRect.width * 2 - reducedHeight = boundingRect.height / 3 + reducedWidth = Math.max(boundingRect.width * 2, 20 * baseMultiplier) + reducedHeight = boundingRect.height } else { - // 가로선의 경우: 세로 선택범위 2배 - reducedWidth = boundingRect.width / 3 - reducedHeight = boundingRect.height * 2 + reducedWidth = boundingRect.width + reducedHeight = Math.max(boundingRect.height * 2, 20 * baseMultiplier) } // 축소된 영역의 중심점 계산 @@ -219,9 +254,6 @@ export const QLine = fabric.util.createClass(fabric.Line, { const bottom = centerY + reducedHeight / 2 // 점이 축소된 영역 내에 있는지 확인 - return point.x >= left && - point.x <= right && - point.y >= top && - point.y <= bottom + return localPoint.x >= left && localPoint.x <= right && localPoint.y >= top && localPoint.y <= bottom }, }) diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index 0315ac76..c94bb316 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -689,13 +689,46 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { return intersects % 2 === 1 }, + setCoords: function () { + // 부모 클래스의 setCoords 호출 + this.callSuper('setCoords') + + // QPolygon의 경우 추가 처리 - 항상 강제로 재계산 + if (this.canvas) { + // 모든 좌표 관련 캐시 초기화 + delete this.oCoords + delete this.aCoords + delete this.__corner + + // 다시 부모 setCoords 호출 + this.callSuper('setCoords') + + // 한 번 더 강제로 bounding rect 재계산 + this._clearCache && this._clearCache() + } + }, + containsPoint: function (point) { + // 먼저 좌표 업데이트 + this.setCoords() + + // 캔버스 줌과 viewport transform 고려한 좌표 변환 + let localPoint = point + if (this.canvas) { + const vpt = this.canvas.viewportTransform + if (vpt) { + // viewport transform 역변환 + const inverted = fabric.util.invertTransform(vpt) + localPoint = fabric.util.transformPoint(point, inverted) + } + } + if (this.name === POLYGON_TYPE.ROOF && this.isFixed) { - const isInside = this.inPolygon(point) + const isInside = this.inPolygon(localPoint) this.set('selectable', isInside) return isInside } else { - return this.callSuper('containsPoint', point) + return this.callSuper('containsPoint', localPoint) } }, diff --git a/src/hooks/useEvent.js b/src/hooks/useEvent.js index 43328376..cf1d1951 100644 --- a/src/hooks/useEvent.js +++ b/src/hooks/useEvent.js @@ -16,7 +16,6 @@ import { useTempGrid } from '@/hooks/useTempGrid' import { gridColorState } from '@/store/gridAtom' import { gridDisplaySelector } from '@/store/settingAtom' import { MENU, POLYGON_TYPE } from '@/common/common' -import useMenu from '@/hooks/common/useMenu' export function useEvent() { const canvas = useRecoilValue(canvasState) @@ -105,6 +104,10 @@ export function useEvent() { canvas.setViewportTransform(canvas.viewportTransform) canvas.requestRenderAll() + canvas.getObjects().forEach((obj) => { + obj.setCoords() + }) + // 이벤트의 기본 동작 방지 (스크롤 방지) opt.e.preventDefault() opt.e.stopPropagation()