From bf681dccba2a314d2fda6b7fa2b86e3412e004d5 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Tue, 2 Dec 2025 11:05:03 +0900 Subject: [PATCH] =?UTF-8?q?=EC=A0=80=EC=9E=A5=20=EC=8B=9C=20=EC=A4=8C=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/common.js | 3 +- src/components/floor-plan/CanvasFrame.jsx | 12 +++++- src/components/floor-plan/CanvasMenu.jsx | 25 +++++++----- src/hooks/common/useCommonUtils.js | 46 ++++++++++++++++++++++- src/hooks/useCanvasEvent.js | 32 ++++++++++++++-- src/util/canvas-util.js | 6 +-- 6 files changed, 104 insertions(+), 20 deletions(-) diff --git a/src/common/common.js b/src/common/common.js index 4220e5f5..60a57c76 100644 --- a/src/common/common.js +++ b/src/common/common.js @@ -219,7 +219,8 @@ export const SAVE_KEY = [ 'originWidth', 'originHeight', 'skeletonLines', - 'skeleton' + 'skeleton', + 'viewportTransform', ] export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype, fabric.Group.prototype] diff --git a/src/components/floor-plan/CanvasFrame.jsx b/src/components/floor-plan/CanvasFrame.jsx index 4b718c05..63dc523a 100644 --- a/src/components/floor-plan/CanvasFrame.jsx +++ b/src/components/floor-plan/CanvasFrame.jsx @@ -2,7 +2,7 @@ import { useContext, useEffect, useRef } from 'react' -import { useRecoilValue, useResetRecoilState } from 'recoil' +import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil' import QContextMenu from '@/components/common/context-menu/QContextMenu' import PanelBatchStatistics from '@/components/floor-plan/modal/panelBatch/PanelBatchStatistics' @@ -11,7 +11,7 @@ import { useCanvas } from '@/hooks/useCanvas' import { usePlan } from '@/hooks/usePlan' import { useContextMenu } from '@/hooks/useContextMenu' import { useCanvasConfigInitialize } from '@/hooks/common/useCanvasConfigInitialize' -import { currentMenuState } from '@/store/canvasAtom' +import { canvasZoomState, currentMenuState } from '@/store/canvasAtom' import { totalDisplaySelector } from '@/store/settingAtom' import { POLYGON_TYPE } from '@/common/common' import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider' @@ -50,6 +50,7 @@ export default function CanvasFrame() { const resetSeriesState = useResetRecoilState(seriesState) const resetModelsState = useResetRecoilState(modelsState) const resetCompasDeg = useResetRecoilState(compasDegAtom) + const [zoom, setCanvasZoom] = useRecoilState(canvasZoomState) const resetSelectedModelsState = useResetRecoilState(selectedModelsState) const resetPcsCheckState = useResetRecoilState(pcsCheckState) const { handleModuleSelectionTotal } = useCanvasPopupStatusController() @@ -67,6 +68,13 @@ export default function CanvasFrame() { canvasLoadInit() //config된 상태로 캔버스 객체를 그린다 canvas?.renderAll() // 캔버스를 다시 그립니다. + if (canvas.viewportTransform) { + if (canvas.viewportTransform[0] !== 1) { + setCanvasZoom(Number((canvas.viewportTransform[0] * 100).toFixed(0))) + } + } + canvas.originViewPortTransform = canvas.viewportTransform + if (canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE).length > 0) { setTimeout(() => { setSelectedMenu('module') diff --git a/src/components/floor-plan/CanvasMenu.jsx b/src/components/floor-plan/CanvasMenu.jsx index 6ddd94c7..97d878a0 100644 --- a/src/components/floor-plan/CanvasMenu.jsx +++ b/src/components/floor-plan/CanvasMenu.jsx @@ -2,7 +2,7 @@ import { useContext, useEffect, useState } from 'react' -import { usePathname, useRouter, useSearchParams } from 'next/navigation' +import { usePathname, useRouter } from 'next/navigation' import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil' @@ -25,17 +25,18 @@ import { useCommonUtils } from '@/hooks/common/useCommonUtils' import useMenu from '@/hooks/common/useMenu' import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController' import { useAxios } from '@/hooks/useAxios' -import { canvasSettingState, canvasState, canvasZoomState, currentMenuState, verticalHorizontalModeState, currentCanvasPlanState } from '@/store/canvasAtom' +import { + canvasSettingState, + canvasState, + canvasZoomState, + currentCanvasPlanState, + currentMenuState, + verticalHorizontalModeState, +} from '@/store/canvasAtom' import { sessionStore } from '@/store/commonAtom' import { outerLinePointsState } from '@/store/outerLineAtom' import { appMessageStore, globalLocaleStore } from '@/store/localeAtom' -import { - addedRoofsState, - basicSettingState, - corridorDimensionSelector, - selectedRoofMaterialSelector, - settingModalFirstOptionsState, -} from '@/store/settingAtom' +import { addedRoofsState, basicSettingState, selectedRoofMaterialSelector, settingModalFirstOptionsState } from '@/store/settingAtom' import { placementShapeDrawingPointsState } from '@/store/placementShapeDrawingAtom' import { commonUtilsState } from '@/store/commonUtilsAtom' import { menusState } from '@/store/menuAtom' @@ -51,6 +52,7 @@ import { QcastContext } from '@/app/QcastProvider' import { useRoofFn } from '@/hooks/common/useRoofFn' import { usePolygon } from '@/hooks/usePolygon' import { useTrestle } from '@/hooks/module/useTrestle' + export default function CanvasMenu(props) { const [currentCanvasPlan, setCurrentCanvasPlan] = useRecoilState(currentCanvasPlanState) const { selectedMenu, setSelectedMenu } = props @@ -515,7 +517,10 @@ export default function CanvasMenu(props) { if (createUser === 'T01' && sessionState.storeId !== 'T01') { setAllButtonStyles('none') } else { - setEstimateContextState({ tempFlg: estimateRecoilState.tempFlg, lockFlg: estimateRecoilState.lockFlg }) + setEstimateContextState({ + tempFlg: estimateRecoilState.tempFlg, + lockFlg: estimateRecoilState.lockFlg, + }) handleButtonStyles(estimateRecoilState.tempFlg, estimateRecoilState.lockFlg, estimateContextState.docNo) } } diff --git a/src/hooks/common/useCommonUtils.js b/src/hooks/common/useCommonUtils.js index 7c53f98c..c878bafd 100644 --- a/src/hooks/common/useCommonUtils.js +++ b/src/hooks/common/useCommonUtils.js @@ -31,8 +31,11 @@ export function useCommonUtils() { useEffect(() => { commonTextMode() if (commonUtils.dimension) { + generateTempGrid() commonDimensionMode() return + } else { + removeTempGrid() } if (commonUtils.distance) { commonDistanceMode() @@ -655,7 +658,7 @@ export function useCommonUtils() { if (obj.name === 'roof') { clonedObj.setCoords() clonedObj.fire('modified') - clonedObj.fire('polygonMoved') + // clonedObj.fire('polygonMoved') clonedObj.set({ direction: obj.direction, directionText: obj.directionText, @@ -905,6 +908,45 @@ export function useCommonUtils() { } } + const generateTempGrid = () => { + if (!canvas) return + + const objects = canvas.getObjects().filter((obj) => ['QPolygon'].includes(obj.type)) + const gridLines = [] + + objects.forEach((obj) => { + const lines = obj.lines + + lines.forEach((line) => { + const gridLine = new fabric.Line([line.x1, line.y1, line.x2, line.y2], { + stroke: 'gray', + strokeWidth: 1, + selectable: false, + evented: false, + opacity: 0.5, + name: 'tempGrid', + direction: line.x1 === line.x2 ? 'vertical' : 'horizontal', + visible: false, + }) + gridLines.push(gridLine) + }) + }) + + gridLines.forEach((line) => { + canvas.add(line) + }) + + canvas.renderAll() + } + + const removeTempGrid = () => { + if (!canvas) return + + const tempGrids = canvas.getObjects().filter((obj) => obj.name === 'tempGrid' && !obj.visible) + tempGrids.forEach((grid) => canvas.remove(grid)) + canvas.renderAll() + } + return { commonFunctions, dimensionSettings, @@ -916,5 +958,7 @@ export function useCommonUtils() { editText, changeDimensionExtendLine, deleteOuterLineObject, + generateTempGrid, + removeTempGrid, } } diff --git a/src/hooks/useCanvasEvent.js b/src/hooks/useCanvasEvent.js index d991a2cf..cc4ac608 100644 --- a/src/hooks/useCanvasEvent.js +++ b/src/hooks/useCanvasEvent.js @@ -402,7 +402,8 @@ export function useCanvasEvent() { } } else { zoom = canvasZoom - 10 - if (zoom < 10) { //50%->10% + if (zoom < 10) { + //50%->10% return } } @@ -412,8 +413,33 @@ export function useCanvasEvent() { const handleZoomClear = () => { setCanvasZoom(100) - canvas.set({ zoom: 1 }) - canvas.viewportTransform = [1, 0, 0, 1, 0, 0] + + zoomToAllObjects() + canvas.renderAll() + } + + const zoomToAllObjects = () => { + const objects = canvas.getObjects().filter((obj) => obj.visible) + if (objects.length === 0) return + + let minX = Infinity, + minY = Infinity + let maxX = -Infinity, + maxY = -Infinity + + objects.forEach((obj) => { + const bounds = obj.getBoundingRect() + minX = Math.min(minX, bounds.left) + minY = Math.min(minY, bounds.top) + maxX = Math.max(maxX, bounds.left + bounds.width) + maxY = Math.max(maxY, bounds.top + bounds.height) + }) + + const centerX = (minX + maxX) / 2 + const centerY = (minY + maxY) / 2 + const centerPoint = new fabric.Point(centerX, centerY) + + canvas.zoomToPoint(centerPoint, 1) canvas.renderAll() } diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js index 243936da..b2ee57db 100644 --- a/src/util/canvas-util.js +++ b/src/util/canvas-util.js @@ -269,7 +269,7 @@ export const getDegreeByChon = (chon) => { * @returns {number} */ export const getChonByDegree = (degree) => { - return Number((Math.tan((degree * Math.PI) / 180) * 10).toFixed(1)) + return Number((Math.tan((degree * Math.PI) / 180) * 10).toFixed(2)) } /** @@ -1036,11 +1036,11 @@ export const getDegreeInOrientation = (degree) => { { min: -51, max: -37, value: -45 }, { min: -36, max: -22, value: -30 }, { min: -21, max: -7, value: -15 }, - { min: -6, max: 0, value: 0 } + { min: -6, max: 0, value: 0 }, ] // 해당 범위에 맞는 값 찾기 - const range = degreeRanges.find(range => degree >= range.min && degree <= range.max) + const range = degreeRanges.find((range) => degree >= range.min && degree <= range.max) return range ? range.value : degree }