diff --git a/src/common/common.js b/src/common/common.js new file mode 100644 index 00000000..ae00ec78 --- /dev/null +++ b/src/common/common.js @@ -0,0 +1,20 @@ +export const Mode = { + DRAW_LINE: 'drawLine', // 기준선 긋기모드` + EDIT: 'edit', + TEMPLATE: 'template', + PATTERNA: 'patterna', + PATTERNB: 'patternb', + TEXTBOX: 'textbox', + DRAW_RECT: 'drawRect', + ROOF_PATTERN: 'roofPattern', //지붕패턴 모드 + ROOF_TRESTLE: 'roofTrestle', //지붕가대 모드 + FILL_CELLS: 'fillCells', //태양광셀 모드 + CELL_POWERCON: 'cellPowercon', //파워콘 + DRAW_HELP_LINE: 'drawHelpLine', // 보조선 그리기 모드 지붕 존재해야함 + DEFAULT: 'default', +} + +export const LineType = { + EAVES: 'eaves', // 처마 + RIDGE: 'ridge', // 용마루.... +} diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index 102a7bea..0dd124cc 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -2,7 +2,7 @@ import { useCanvas } from '@/hooks/useCanvas' import { useEffect, useState, useRef } from 'react' -import { Mode, useMode } from '@/hooks/useMode' +import { useMode } from '@/hooks/useMode' import { Button } from '@nextui-org/react' import RangeSlider from './ui/RangeSlider' import { useRecoilState, useRecoilValue } from 'recoil' @@ -22,6 +22,7 @@ import { calculateIntersection } from '@/util/canvas-util' import { QPolygon } from '@/components/fabric/QPolygon' import ThumbnailList from './ui/ThumbnailLIst' import CanvasWithContextMenu from '@/util/context-util' +import { Mode } from '@/common/common' export default function Roof2() { const { canvas, handleRedo, handleUndo, setCanvasBackgroundWithDots, saveImage, addCanvas } = useCanvas('canvas') diff --git a/src/components/fabric/QLine.js b/src/components/fabric/QLine.js index 08f147a2..9604ec7c 100644 --- a/src/components/fabric/QLine.js +++ b/src/components/fabric/QLine.js @@ -104,6 +104,7 @@ export const QLine = fabric.util.createClass(fabric.Line, { const maxX = this.left + this.width const minY = this.top const maxY = this.top + this.length + const degree = (Math.atan2(y2 - y1, x2 - x1) * 180) / Math.PI const text = new fabric.Textbox(this.length.toFixed(0).toString(), { left: left, @@ -114,6 +115,7 @@ export const QLine = fabric.util.createClass(fabric.Line, { minY, maxY, parentDirection: this.direction, + parentDegree: degree, parentId: this.id, editable: false, selectable: true, diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index 2990feb7..28a7d84a 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -173,6 +173,8 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { const midPoint = new fabric.Point((start.x + end.x) / 2, (start.y + end.y) / 2) + const degree = (Math.atan2(dy, dx) * 180) / Math.PI + // Create new text object if it doesn't exist const text = new fabric.Text(length.toFixed(0), { left: midPoint.x, @@ -184,6 +186,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { minY: Math.min(start.y, end.y), maxY: Math.max(start.y, end.y), parentDirection: getDirectionByPoint(start, end), + parentDegree: degree, dirty: true, editable: false, selectable: true, diff --git a/src/hooks/useCanvas.js b/src/hooks/useCanvas.js index 376ab280..cde867b1 100644 --- a/src/hooks/useCanvas.js +++ b/src/hooks/useCanvas.js @@ -10,6 +10,7 @@ import { QPolygon } from '@/components/fabric/QPolygon' import { defineQLine } from '@/util/qline-utils' import { defineQPloygon } from '@/util/qpolygon-utils' import { writeImage } from '@/lib/canvas' +import { useCanvasEvent } from '@/hooks/useCanvasEvent' export function useCanvas(id) { const [canvas, setCanvas] = useState() @@ -17,7 +18,7 @@ export function useCanvas(id) { const [history, setHistory] = useState([]) const [canvasSize] = useRecoilState(canvasSizeState) const [fontSize] = useRecoilState(fontSizeState) - const points = useRef([]) + const { setCanvasForEvent, attachDefaultEventOnCanvas } = useCanvasEvent() /** * 처음 셋팅 @@ -31,6 +32,8 @@ export function useCanvas(id) { }) setCanvas(c) + setCanvasForEvent(c) + return () => { c.dispose() } @@ -38,8 +41,7 @@ export function useCanvas(id) { useEffect(() => { // canvas 사이즈가 변경되면 다시 - removeEventOnCanvas() - addEventOnCanvas() + attachDefaultEventOnCanvas() }, [canvasSize]) useEffect(() => { @@ -65,113 +67,10 @@ export function useCanvas(id) { useEffect(() => { if (canvas) { initialize() - removeEventOnCanvas() - addEventOnCanvas() + attachDefaultEventOnCanvas() } }, [canvas]) - const addEventOnCanvas = () => { - canvas?.on('object:added', onChange) - canvas?.on('object:added', addEventOnObject) - canvas?.on('object:modified', onChange) - canvas?.on('object:removed', onChange) - canvas?.on('object:added', () => { - document.addEventListener('keydown', handleKeyDown) - }) - - canvas?.on('mouse:move', drawMouseLines) - canvas?.on('mouse:down', handleMouseDown) - canvas?.on('mouse:out', removeMouseLines) - } - - const removeEventOnCanvas = () => { - canvas?.off('object:added') - canvas?.off('object:modified') - canvas?.off('object:removed') - canvas?.off('object:added') - canvas?.off('mouse:move') - canvas?.off('mouse:down') - } - - const addEventOnObject = (e) => { - const target = e.target - - if (target.type === 'QPolygon' || target.type === 'QLine') { - const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'lengthText') - textObjs.forEach((obj) => { - obj.bringToFront() - }) - } - - if (target.name === 'cell') { - target.on('mousedown', () => { - if (target.get('selected')) { - target.set({ selected: false }) - target.set({ fill: '#BFFD9F' }) - } else { - target.set({ selected: true }) - target.set({ fill: 'red' }) - } - canvas?.renderAll() - }) - } - - if (target.name === 'trestle') { - target.on('mousedown', () => { - if (target.defense === 'north') { - alert('북쪽은 선택 불가합니다.') - return - } - if (target.get('selected')) { - target.set({ strokeWidth: 1 }) - target.set({ strokeDashArray: [5, 5] }) - target.set({ selected: false }) - } else { - target.set({ strokeWidth: 5 }) - target.set({ strokeDashArray: [0, 0] }) - target.set({ selected: true }) - } - canvas?.renderAll() - }) - } - - if (target.name === 'lengthText') { - const x = target.left - const y = target.top - target.on('selected', (e) => { - Object.keys(target.controls).forEach((controlKey) => { - target.setControlVisible(controlKey, false) - }) - }) - target.on('moving', (e) => { - if (target.parentDirection === 'left' || target.parentDirection === 'right') { - const minX = target.minX - const maxX = target.maxX - - if (target.left <= minX) { - target.set({ left: minX, top: y }) - } else if (target.left >= maxX) { - target.set({ left: maxX, top: y }) - } else { - target.set({ top: y }) - } - } else if (target.parentDirection === 'top' || target.parentDirection === 'bottom') { - const minY = target.minY - const maxY = target.maxY - - if (target.top <= minY) { - target.set({ left: x, top: minY }) - } else if (target.top >= maxY) { - target.set({ left: x, top: maxY }) - } else { - target.set({ left: x }) - } - } - canvas?.renderAll() - }) - } - } - /** * 마우스 포인터의 가이드라인을 제거합니다. */ @@ -220,71 +119,6 @@ export function useCanvas(id) { setIsLocked(false) } - const drawMouseLines = (e) => { - // 현재 마우스 포인터의 위치를 가져옵니다. - const pointer = canvas?.getPointer(e.e) - - // 기존에 그려진 가이드라인을 제거합니다. - removeMouseLines() - - if (canvas?.getActiveObject()) { - return - } - - // 가로선을 그립니다. - const horizontalLine = new fabric.Line([0, pointer.y, canvasSize.horizontal, pointer.y], { - stroke: 'black', - strokeWidth: 1, - selectable: false, - name: 'mouseLine', - }) - - // 세로선을 그립니다. - const verticalLine = new fabric.Line([pointer.x, 0, pointer.x, canvasSize.vertical], { - stroke: 'black', - strokeWidth: 1, - selectable: false, - name: 'mouseLine', - }) - - // 선들을 캔버스에 추가합니다. - canvas?.add(horizontalLine, verticalLine) - - // 캔버스를 다시 그립니다. - canvas?.renderAll() - } - - const handleMouseDown = (e) => { - // 현재 마우스 포인터의 위치를 가져옵니다. - if (canvas?.getActiveObject()) { - points.current = [] - return - } - const pointer = canvas?.getPointer(e.e) - - // 클릭한 위치를 배열에 추가합니다. - points.current.push(pointer) - - // 두 점을 모두 찍었을 때 사각형을 그립니다. - if (points.current.length === 2) { - const rect = new fabric.Rect({ - left: points.current[0].x, - top: points.current[0].y, - width: points.current[1].x - points.current[0].x, - height: points.current[1].y - points.current[0].y, - fill: 'transparent', - stroke: 'black', - strokeWidth: 1, - }) - - // 사각형을 캔버스에 추가합니다. - canvas?.add(rect) - - // 배열을 초기화합니다. - points.current = [] - } - } - /** * 눈금 모양에 맞게 움직이도록 한다. */ @@ -459,50 +293,6 @@ export function useCanvas(id) { canvas?.renderAll() } - /** - * 각종 키보드 이벤트 - * https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values - */ - const handleKeyDown = (e) => { - const key = e.key - - switch (key) { - case 'Delete': - case 'Backspace': - handleDelete() - break - case 'Down': // IE/Edge에서 사용되는 값 - case 'ArrowDown': - // "아래 화살표" 키가 눌렸을 때의 동작입니다. - moveDown() - break - case 'Up': // IE/Edge에서 사용되는 값 - case 'ArrowUp': - // "위 화살표" 키가 눌렸을 때의 동작입니다. - moveUp() - break - case 'Left': // IE/Edge에서 사용되는 값 - case 'ArrowLeft': - // "왼쪽 화살표" 키가 눌렸을 때의 동작입니다. - moveLeft() - break - case 'Right': // IE/Edge에서 사용되는 값 - case 'ArrowRight': - // "오른쪽 화살표" 키가 눌렸을 때의 동작입니다. - moveRight() - break - case 'Enter': - // "enter" 또는 "return" 키가 눌렸을 때의 동작입니다. - break - case 'Esc': // IE/Edge에서 사용되는 값 - case 'Escape': - break - default: - return // 키 이벤트를 처리하지 않는다면 종료합니다. - } - e.preventDefault() - } - const handleRotate = (degree = 45) => { const target = canvas?.getActiveObject() diff --git a/src/hooks/useCanvasEvent.js b/src/hooks/useCanvasEvent.js new file mode 100644 index 00000000..c0620468 --- /dev/null +++ b/src/hooks/useCanvasEvent.js @@ -0,0 +1,277 @@ +import { useEffect, useState } from 'react' +import { fabric } from 'fabric' +import { useRecoilValue } from 'recoil' +import { canvasSizeState, modeState } from '@/store/canvasAtom' + +// 캔버스에 필요한 이벤트 +export function useCanvasEvent() { + const [canvas, setCanvasForEvent] = useState(null) + const canvasSize = useRecoilValue(canvasSizeState) + + // 기본적인 이벤트 필요시 추가 + const attachDefaultEventOnCanvas = () => { + removeEventOnCanvas() + canvas?.on('object:added', onChange) + canvas?.on('object:added', addEventOnObject) + canvas?.on('object:modified', onChange) + canvas?.on('object:removed', onChange) + canvas?.on('object:added', () => { + document.addEventListener('keydown', handleKeyDown) + }) + + canvas?.on('mouse:move', drawMouseLines) + canvas?.on('mouse:out', removeMouseLines) + } + + const onChange = (e) => { + const target = e.target + if (target) { + // settleDown(target) + } + } + + const removeEventOnCanvas = () => { + canvas?.off('object:added') + canvas?.off('object:modified') + canvas?.off('object:removed') + canvas?.off('object:added') + canvas?.off('mouse:move') + canvas?.off('mouse:down') + } + + const addEventOnObject = (e) => { + const target = e.target + + if (target.type === 'QPolygon' || target.type === 'QLine') { + const textObjs = canvas?.getObjects().filter((obj) => obj.name === 'lengthText') + textObjs.forEach((obj) => { + obj.bringToFront() + }) + } + + if (target.name === 'cell') { + target.on('mousedown', () => { + if (target.get('selected')) { + target.set({ selected: false }) + target.set({ fill: '#BFFD9F' }) + } else { + target.set({ selected: true }) + target.set({ fill: 'red' }) + } + canvas?.renderAll() + }) + } + + if (target.name === 'trestle') { + target.on('mousedown', () => { + if (target.defense === 'north') { + alert('북쪽은 선택 불가합니다.') + return + } + if (target.get('selected')) { + target.set({ strokeWidth: 1 }) + target.set({ strokeDashArray: [5, 5] }) + target.set({ selected: false }) + } else { + target.set({ strokeWidth: 5 }) + target.set({ strokeDashArray: [0, 0] }) + target.set({ selected: true }) + } + canvas?.renderAll() + }) + } + + if (target.name === 'lengthText') { + const x = target.left + const y = target.top + target.on('selected', (e) => { + Object.keys(target.controls).forEach((controlKey) => { + target.setControlVisible(controlKey, false) + }) + }) + target.on('moving', (e) => { + if (target.parentDirection === 'left' || target.parentDirection === 'right') { + const minX = target.minX + const maxX = target.maxX + + if (target.left <= minX) { + target.set({ left: minX, top: y }) + } else if (target.left >= maxX) { + target.set({ left: maxX, top: y }) + } else { + target.set({ top: y }) + } + } else if (target.parentDirection === 'top' || target.parentDirection === 'bottom') { + const minY = target.minY + const maxY = target.maxY + + if (target.top <= minY) { + target.set({ left: x, top: minY }) + } else if (target.top >= maxY) { + target.set({ left: x, top: maxY }) + } else { + target.set({ left: x }) + } + } + canvas?.renderAll() + }) + } + } + + // 마우스 가로, 세로선 그리기 + const drawMouseLines = (e) => { + // 현재 마우스 포인터의 위치를 가져옵니다. + const pointer = canvas?.getPointer(e.e) + + // 기존에 그려진 가이드라인을 제거합니다. + removeMouseLines() + + if (canvas?.getActiveObject()) { + return + } + + // 가로선을 그립니다. + const horizontalLine = new fabric.Line([0, pointer.y, canvasSize.horizontal, pointer.y], { + stroke: 'black', + strokeWidth: 1, + selectable: false, + name: 'mouseLine', + }) + + // 세로선을 그립니다. + const verticalLine = new fabric.Line([pointer.x, 0, pointer.x, canvasSize.vertical], { + stroke: 'black', + strokeWidth: 1, + selectable: false, + name: 'mouseLine', + }) + + // 선들을 캔버스에 추가합니다. + canvas?.add(horizontalLine, verticalLine) + + // 캔버스를 다시 그립니다. + canvas?.renderAll() + } + + //가로, 세로 선 삭제 + const removeMouseLines = () => { + if (canvas?._objects.length > 0) { + const mouseLines = canvas?._objects.filter((obj) => obj.name === 'mouseLine') + mouseLines.forEach((item) => canvas?.remove(item)) + } + canvas?.renderAll() + } + + /** + * 각종 키보드 이벤트 + * https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values + */ + const handleKeyDown = (e) => { + const key = e.key + + switch (key) { + case 'Delete': + case 'Backspace': + handleDelete() + break + case 'Down': // IE/Edge에서 사용되는 값 + case 'ArrowDown': + // "아래 화살표" 키가 눌렸을 때의 동작입니다. + moveDown() + break + case 'Up': // IE/Edge에서 사용되는 값 + case 'ArrowUp': + // "위 화살표" 키가 눌렸을 때의 동작입니다. + moveUp() + break + case 'Left': // IE/Edge에서 사용되는 값 + case 'ArrowLeft': + // "왼쪽 화살표" 키가 눌렸을 때의 동작입니다. + moveLeft() + break + case 'Right': // IE/Edge에서 사용되는 값 + case 'ArrowRight': + // "오른쪽 화살표" 키가 눌렸을 때의 동작입니다. + moveRight() + break + case 'Enter': + // "enter" 또는 "return" 키가 눌렸을 때의 동작입니다. + break + case 'Esc': // IE/Edge에서 사용되는 값 + case 'Escape': + break + default: + return // 키 이벤트를 처리하지 않는다면 종료합니다. + } + e.preventDefault() + } + + const moveDown = () => { + const targetObj = canvas?.getActiveObject() + if (!targetObj) { + return + } + + let top = targetObj.top + 10 + + if (top > canvasSize.vertical) { + top = canvasSize.vertical + } + + targetObj.set({ top: top }) + canvas?.renderAll() + } + + const moveUp = () => { + const targetObj = canvas?.getActiveObject() + if (!targetObj) { + return + } + + let top = targetObj.top - 10 + + if (top < 0) { + top = 0 + } + + targetObj.set({ top: top }) + canvas?.renderAll() + } + + const moveLeft = () => { + const targetObj = canvas?.getActiveObject() + if (!targetObj) { + return + } + + let left = targetObj.left - 10 + + if (left < 0) { + left = 0 + } + + targetObj.set({ left: left }) + canvas?.renderAll() + } + + const moveRight = () => { + const targetObj = canvas?.getActiveObject() + if (!targetObj) { + return + } + + let left = targetObj.left + 10 + + if (left > canvasSize.horizontal) { + left = canvasSize.horizontal + } + + targetObj.set({ left: left }) + canvas?.renderAll() + } + + return { + setCanvasForEvent, + attachDefaultEventOnCanvas, + } +} diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index 9ac623a1..3954190a 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -23,6 +23,7 @@ import { templateTypeState, wallState, compassState, + modeState, } from '@/store/canvasAtom' import { QLine } from '@/components/fabric/QLine' import { fabric } from 'fabric' @@ -30,25 +31,10 @@ import { QPolygon } from '@/components/fabric/QPolygon' import offsetPolygon from '@/util/qpolygon-utils' import { isObjectNotEmpty } from '@/util/common-utils' import * as turf from '@turf/turf' - -export const Mode = { - DRAW_LINE: 'drawLine', // 기준선 긋기모드` - EDIT: 'edit', - TEMPLATE: 'template', - PATTERNA: 'patterna', - PATTERNB: 'patternb', - TEXTBOX: 'textbox', - DRAW_RECT: 'drawRect', - ROOF_PATTERN: 'roofPattern', //지붕패턴 모드 - ROOF_TRESTLE: 'roofTrestle', //지붕가대 모드 - FILL_CELLS: 'fillCells', //태양광셀 모드 - CELL_POWERCON: 'cellPowercon', //파워콘 - DRAW_HELP_LINE: 'drawHelpLine', // 보조선 그리기 모드 지붕 존재해야함 - DEFAULT: 'default', -} +import { Mode } from '@/common/common' export function useMode() { - const [mode, setMode] = useState() + const [mode, setMode] = useRecoilState(modeState) const points = useRef([]) const historyPoints = useRef([]) const historyLines = useRef([]) @@ -233,49 +219,28 @@ export function useMode() { canvas?.renderAll() } - const addEvent = (mode) => { + // 각 모드에 따른 마우스 이벤트 변경 + const changeMouseEvent = (mode) => { + canvas?.off('mouse:down') switch (mode) { case 'drawLine': - drawLineMode() + canvas?.on('mouse:down', mouseEvent.drawLineMode) break case 'edit': - editMode() - break - case 'template': - templateMode() - break - case 'patterna': - applyTemplateA() - break - case 'patternb': - applyTemplateB() + canvas?.on('mouse:down', mouseEvent.editMode) break case 'textbox': - textboxMode() + canvas?.on('mouse:down', mouseEvent.textboxMode) break case 'drawRect': - drawRectMode() - break - case 'roofPattern': - makeRoofPatternPolygon() - break - case 'roofTrestle': - makeRoofTrestle() - break - case 'fillCells': - makeRoofFillCells() - break - case 'cellPowercon': - makeCellPowercon() + canvas?.on('mouse:down', mouseEvent.drawRectMode) break case 'drawHelpLine': canvas?.off('selection:created', addSelectCreatedEvent) canvas?.off('selection:cleared', addSelectClearedEvent) canvas?.on('selection:created', addSelectCreatedEvent) canvas?.on('selection:cleared', addSelectClearedEvent) - drawHelpLineMode() break - case 'default': canvas?.off('mouse:down') break @@ -504,19 +469,62 @@ export function useMode() { } const changeMode = (canvas, mode) => { - setEndPoint(null) - pointCount.current = 0 - setMode(mode) // mode변경 시 이전 이벤트 제거 setCanvas(canvas) - canvas?.off('mouse:down') - addEvent(mode) + changeMouseEvent(mode) + + switch (mode) { + case 'template': + templateMode() + break + case 'patterna': + applyTemplateA() + break + case 'patternb': + applyTemplateB() + break + case 'roofPattern': + makeRoofPatternPolygon() + break + case 'roofTrestle': + makeRoofTrestle() + break + case 'fillCells': + makeRoofFillCells() + break + case 'cellPowercon': + makeCellPowercon() + break + case 'drawHelpLine': + drawHelpLineMode() + break + case 'default': + clearEditMode() + break + } } - const editMode = () => { - canvas?.on('mouse:down', function (options) { + const mouseEvent = { + drawLineMode: (options) => { + const pointer = canvas?.getPointer(options.e) + + const line = new QLine( + [pointer.x, 0, pointer.x, canvas.height], // y축에 1자 선을 그립니다. + { + stroke: 'black', + strokeWidth: 2, + viewLengthText: true, + selectable: false, + fontSize: fontSize, + }, + ) + + canvas?.add(line) + canvas?.renderAll() + }, + editMode: (options) => { const pointer = canvas?.getPointer(options.e) const circle = new fabric.Circle({ radius: 5, @@ -585,7 +593,62 @@ export function useMode() { } canvas?.renderAll() - }) + }, + textboxMode: (options) => { + if (canvas?.getActiveObject()?.type === 'textbox') return + const pointer = canvas?.getPointer(options.e) + + const textbox = new fabric.Textbox('텍스트를 입력하세요', { + left: pointer.x, + top: pointer.y, + width: 150, // 텍스트박스의 너비를 설정합니다. + fontSize: fontSize, // 텍스트의 크기를 설정합니다. + }) + + canvas?.add(textbox) + canvas?.setActiveObject(textbox) // 생성된 텍스트박스를 활성 객체로 설정합니다. + canvas?.renderAll() + // textbox가 active가 풀린 경우 editing mode로 변경 + textbox?.on('editing:exited', function () { + changeMode(canvas, Mode.EDIT) + }) + }, + drawRectMode: () => { + let rect, isDown, origX, origY + canvas.on('mouse:down', function (o) { + isDown = true + const pointer = canvas.getPointer(o.e) + origX = pointer.x + origY = pointer.y + rect = new fabric.Rect({ + left: origX, + top: origY, + originX: 'left', + originY: 'top', + width: pointer.x - origX, + height: pointer.y - origY, + angle: 0, + fill: 'transparent', + stroke: 'black', + transparentCorners: false, + }) + canvas.add(rect) + }) + + canvas.on('mouse:move', function (o) { + if (!isDown) return + const pointer = canvas.getPointer(o.e) + if (origX > pointer.x) { + rect.set({ left: Math.abs(pointer.x) }) + } + if (origY > pointer.y) { + rect.set({ top: Math.abs(pointer.y) }) + } + + rect.set({ width: Math.abs(origX - pointer.x) }) + rect.set({ height: Math.abs(origY - pointer.y) }) + }) + }, } const pushHistoryLine = (line) => { @@ -643,86 +706,6 @@ export function useMode() { setTemplateType(1) } } - - const textboxMode = () => { - canvas?.on('mouse:down', function (options) { - if (canvas?.getActiveObject()?.type === 'textbox') return - const pointer = canvas?.getPointer(options.e) - - const textbox = new fabric.Textbox('텍스트를 입력하세요', { - left: pointer.x, - top: pointer.y, - width: 150, // 텍스트박스의 너비를 설정합니다. - fontSize: fontSize, // 텍스트의 크기를 설정합니다. - }) - - canvas?.add(textbox) - canvas?.setActiveObject(textbox) // 생성된 텍스트박스를 활성 객체로 설정합니다. - canvas?.renderAll() - // textbox가 active가 풀린 경우 editing mode로 변경 - textbox?.on('editing:exited', function () { - changeMode(canvas, Mode.EDIT) - }) - }) - } - - const drawLineMode = () => { - canvas?.on('mouse:down', function (options) { - const pointer = canvas?.getPointer(options.e) - - const line = new QLine( - [pointer.x, 0, pointer.x, canvas.height], // y축에 1자 선을 그립니다. - { - stroke: 'black', - strokeWidth: 2, - viewLengthText: true, - selectable: false, - fontSize: fontSize, - }, - ) - - canvas?.add(line) - canvas?.renderAll() - }) - } - - const drawRectMode = () => { - let rect, isDown, origX, origY - canvas.on('mouse:down', function (o) { - isDown = true - const pointer = canvas.getPointer(o.e) - origX = pointer.x - origY = pointer.y - rect = new fabric.Rect({ - left: origX, - top: origY, - originX: 'left', - originY: 'top', - width: pointer.x - origX, - height: pointer.y - origY, - angle: 0, - fill: 'transparent', - stroke: 'black', - transparentCorners: false, - }) - canvas.add(rect) - }) - - canvas.on('mouse:move', function (o) { - if (!isDown) return - const pointer = canvas.getPointer(o.e) - if (origX > pointer.x) { - rect.set({ left: Math.abs(pointer.x) }) - } - if (origY > pointer.y) { - rect.set({ top: Math.abs(pointer.y) }) - } - - rect.set({ width: Math.abs(origX - pointer.x) }) - rect.set({ height: Math.abs(origY - pointer.y) }) - }) - } - /** * 두 점을 연결하는 선과 길이를 그립니다. * a : 시작점, b : 끝점 @@ -818,6 +801,7 @@ export function useMode() { canvas?.clear() startPoint.current = null setEndPoint(null) + pointCount.current = 0 setTemplateType(0) points.current = [] historyPoints.current = [] @@ -826,6 +810,15 @@ export function useMode() { setSelectedCellRoofArray([]) //셀 그린거 삭제 } + const clearEditMode = () => { + startPoint.current = null + setEndPoint(null) + pointCount.current = 0 + points.current = [] + historyPoints.current = [] + historyLines.current = [] + } + const zoomIn = () => { canvas?.setZoom(canvas.getZoom() + 0.1) setZoom(Math.round(zoom + 10)) diff --git a/src/store/canvasAtom.js b/src/store/canvasAtom.js index fa4d2770..b8a17a3b 100644 --- a/src/store/canvasAtom.js +++ b/src/store/canvasAtom.js @@ -5,6 +5,11 @@ export const textState = atom({ default: 'test text', }) +export const modeState = atom({ + key: 'modeState', + default: 'default', +}) + export const fontSizeState = atom({ key: 'fontSizeState', default: 16,