event 분리
This commit is contained in:
parent
363f3a1d01
commit
20caabf732
20
src/common/common.js
Normal file
20
src/common/common.js
Normal file
@ -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', // 용마루....
|
||||
}
|
||||
@ -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')
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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()
|
||||
|
||||
|
||||
277
src/hooks/useCanvasEvent.js
Normal file
277
src/hooks/useCanvasEvent.js
Normal file
@ -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,
|
||||
}
|
||||
}
|
||||
@ -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))
|
||||
|
||||
@ -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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user