줌 확대, 축소 시 오브젝트 선택 안되는 현상 수정

This commit is contained in:
hyojun.choi 2025-07-09 16:02:55 +09:00 committed by ysCha
parent 71d92f12b8
commit 0badbef52c
3 changed files with 87 additions and 19 deletions

View File

@ -183,29 +183,64 @@ export const QLine = fabric.util.createClass(fabric.Line, {
return this 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 boundingRect = this.getBoundingRect(true)
// 선의 방향 판단 // 선의 방향 판단
const dx = Math.abs(this.x2 - this.x1) const dx = Math.abs(this.x2 - this.x1)
const dy = Math.abs(this.y2 - this.y1) const dy = Math.abs(this.y2 - this.y1)
const isVertical = dx < dy && dx < 10 // 세로선 (가로 변화가 작음) const isVertical = dx < dy && dx < 10
const isDiagonal = dx > 10 && dy > 10 // 대각선 (가로, 세로 모두 변화) const isDiagonal = dx > 10 && dy > 10
// 줌 레벨에 따른 선택 영역 조정
const zoom = this.canvas ? this.canvas.getZoom() : 1
const baseMultiplier = 1 // 줌이 클수록 선택 영역을 줄임
let reducedWidth, reducedHeight let reducedWidth, reducedHeight
if (isDiagonal) { if (isDiagonal) {
// 대각선의 경우: 1/2 크기 reducedWidth = Math.max(boundingRect.width / 2, 10 * baseMultiplier)
reducedWidth = boundingRect.width / 2 reducedHeight = Math.max(boundingRect.height / 2, 10 * baseMultiplier)
reducedHeight = boundingRect.height / 2
} else if (isVertical) { } else if (isVertical) {
// 세로선의 경우: 가로 선택범위 2배 reducedWidth = Math.max(boundingRect.width * 2, 20 * baseMultiplier)
reducedWidth = boundingRect.width * 2 reducedHeight = boundingRect.height
reducedHeight = boundingRect.height / 3
} else { } else {
// 가로선의 경우: 세로 선택범위 2배 reducedWidth = boundingRect.width
reducedWidth = boundingRect.width / 3 reducedHeight = Math.max(boundingRect.height * 2, 20 * baseMultiplier)
reducedHeight = boundingRect.height * 2
} }
// 축소된 영역의 중심점 계산 // 축소된 영역의 중심점 계산
@ -219,9 +254,6 @@ export const QLine = fabric.util.createClass(fabric.Line, {
const bottom = centerY + reducedHeight / 2 const bottom = centerY + reducedHeight / 2
// 점이 축소된 영역 내에 있는지 확인 // 점이 축소된 영역 내에 있는지 확인
return point.x >= left && return localPoint.x >= left && localPoint.x <= right && localPoint.y >= top && localPoint.y <= bottom
point.x <= right &&
point.y >= top &&
point.y <= bottom
}, },
}) })

View File

@ -689,13 +689,46 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
return intersects % 2 === 1 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) { 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) { if (this.name === POLYGON_TYPE.ROOF && this.isFixed) {
const isInside = this.inPolygon(point) const isInside = this.inPolygon(localPoint)
this.set('selectable', isInside) this.set('selectable', isInside)
return isInside return isInside
} else { } else {
return this.callSuper('containsPoint', point) return this.callSuper('containsPoint', localPoint)
} }
}, },

View File

@ -16,7 +16,6 @@ import { useTempGrid } from '@/hooks/useTempGrid'
import { gridColorState } from '@/store/gridAtom' import { gridColorState } from '@/store/gridAtom'
import { gridDisplaySelector } from '@/store/settingAtom' import { gridDisplaySelector } from '@/store/settingAtom'
import { MENU, POLYGON_TYPE } from '@/common/common' import { MENU, POLYGON_TYPE } from '@/common/common'
import useMenu from '@/hooks/common/useMenu'
export function useEvent() { export function useEvent() {
const canvas = useRecoilValue(canvasState) const canvas = useRecoilValue(canvasState)
@ -105,6 +104,10 @@ export function useEvent() {
canvas.setViewportTransform(canvas.viewportTransform) canvas.setViewportTransform(canvas.viewportTransform)
canvas.requestRenderAll() canvas.requestRenderAll()
canvas.getObjects().forEach((obj) => {
obj.setCoords()
})
// 이벤트의 기본 동작 방지 (스크롤 방지) // 이벤트의 기본 동작 방지 (스크롤 방지)
opt.e.preventDefault() opt.e.preventDefault()
opt.e.stopPropagation() opt.e.stopPropagation()