템플릿모드 선택시 기존 line 제거 후 polygon 생성

This commit is contained in:
hyojun.choi 2024-06-24 19:14:49 +09:00
parent 75368cc645
commit 1cfadf4c3a
2 changed files with 117 additions and 54 deletions

View File

@ -1,11 +1,11 @@
import { useCanvas } from '@/hooks/useCanvas' import { useCanvas } from '@/hooks/useCanvas'
import { useEffect } from 'react' import { useEffect } from 'react'
import { MODE, useMode } from '@/app/mode' import { UseMode, useMode } from '@/hooks/useMode'
export default function Roof2() { export default function Roof2() {
const { canvas, handleRedo, handleUndo, handleClear } = useCanvas('canvas') const { canvas, handleRedo, handleUndo, handleClear } = useCanvas('canvas')
const { mode, changeMode } = useMode() const { mode, changeMode, setCanvas } = useMode()
useEffect(() => { useEffect(() => {
// canvas // canvas
@ -18,32 +18,32 @@ export default function Roof2() {
<> <>
<div className="flex justify-center my-8"> <div className="flex justify-center my-8">
<button <button
className={`w-30 mx-2 p-2 rounded ${mode === MODE.DRAW_LINE ? 'bg-blue-500' : 'bg-gray-500'} text-white`} className={`w-30 mx-2 p-2 rounded ${mode === UseMode.DRAW_LINE ? 'bg-blue-500' : 'bg-gray-500'} text-white`}
onClick={() => changeMode(canvas, MODE.DRAW_LINE)} onClick={() => changeMode(canvas, UseMode.DRAW_LINE)}
> >
기준선 긋기 모드 기준선 긋기 모드
</button> </button>
<button <button
className={`w-30 mx-2 p-2 rounded ${mode === MODE.EDIT ? 'bg-blue-500' : 'bg-gray-500'} text-white`} className={`w-30 mx-2 p-2 rounded ${mode === UseMode.EDIT ? 'bg-blue-500' : 'bg-gray-500'} text-white`}
onClick={() => changeMode(canvas, MODE.EDIT)} onClick={() => changeMode(canvas, UseMode.EDIT)}
> >
에디팅모드 에디팅모드
</button> </button>
<button <button
className={`w-30 mx-2 p-2 rounded ${mode === MODE.TEMPLATE ? 'bg-blue-500' : 'bg-gray-500'} text-white`} className={`w-30 mx-2 p-2 rounded ${mode === UseMode.TEMPLATE ? 'bg-blue-500' : 'bg-gray-500'} text-white`}
onClick={() => changeMode(canvas, MODE.TEMPLATE)} onClick={() => changeMode(canvas, UseMode.TEMPLATE)}
> >
템플릿모드 템플릿모드
</button> </button>
<button <button
className={`w-30 mx-2 p-2 rounded ${mode === MODE.TEXTBOX ? 'bg-blue-500' : 'bg-gray-500'} text-white`} className={`w-30 mx-2 p-2 rounded ${mode === UseMode.TEXTBOX ? 'bg-blue-500' : 'bg-gray-500'} text-white`}
onClick={() => changeMode(canvas, MODE.TEXTBOX)} onClick={() => changeMode(canvas, UseMode.TEXTBOX)}
> >
텍스트박스 모드 텍스트박스 모드
</button> </button>
<button <button
className={`w-30 mx-2 p-2 rounded ${mode === MODE.DRAW_RECT ? 'bg-blue-500' : 'bg-gray-500'} text-white`} className={`w-30 mx-2 p-2 rounded ${mode === UseMode.DRAW_RECT ? 'bg-blue-500' : 'bg-gray-500'} text-white`}
onClick={() => changeMode(canvas, MODE.DRAW_RECT)} onClick={() => changeMode(canvas, UseMode.DRAW_RECT)}
> >
사각형 생성 모드 사각형 생성 모드
</button> </button>

View File

@ -1,6 +1,6 @@
import { useRef, useState } from 'react' import { useRef, useState } from 'react'
export const MODE = { export const UseMode = {
DRAW_LINE: 'drawLine', // 기준선 긋기모드 DRAW_LINE: 'drawLine', // 기준선 긋기모드
EDIT: 'edit', EDIT: 'edit',
TEMPLATE: 'template', TEMPLATE: 'template',
@ -9,27 +9,28 @@ export const MODE = {
} }
export function useMode() { export function useMode() {
const [mode, setMode] = useState(MODE.EDIT) const [mode, setMode] = useState(UseMode.EDIT)
const points = useRef([]) const points = useRef([])
const historyPoints = useRef([]) const historyPoints = useRef([])
const lines = useRef([]) const historyLines = useRef([])
const [canvas, setCanvas] = useState(null)
const addEvent = (canvas, mode) => { const addEvent = (mode) => {
switch (mode) { switch (mode) {
case 'drawLine': case 'drawLine':
drawLineMode(canvas) drawLineMode()
break break
case 'edit': case 'edit':
editMode(canvas) editMode()
break break
case 'template': case 'template':
templateMode(canvas) templateMode()
break break
case 'textbox': case 'textbox':
textboxMode(canvas) textboxMode()
break break
case 'drawRect': case 'drawRect':
drawRectMode(canvas) drawRectMode()
break break
} }
} }
@ -38,15 +39,15 @@ export function useMode() {
setMode(mode) setMode(mode)
// mode변경 시 이전 이벤트 제거 // mode변경 시 이전 이벤트 제거
canvas?.off('mouse:down') canvas?.off('mouse:down')
setCanvas(canvas)
addEvent(canvas, mode) addEvent(mode)
} }
const editMode = (canvas) => { const editMode = () => {
canvas?.on('mouse:down', function (options) { canvas?.on('mouse:down', function (options) {
const pointer = canvas?.getPointer(options.e) const pointer = canvas?.getPointer(options.e)
const circle = new fabric.Circle({ const circle = new fabric.Circle({
radius: 5, radius: 1,
fill: 'transparent', // 원 안을 비웁니다. fill: 'transparent', // 원 안을 비웁니다.
stroke: 'black', // 원 테두리 색상을 검은색으로 설정합니다. stroke: 'black', // 원 테두리 색상을 검은색으로 설정합니다.
left: pointer.x, left: pointer.x,
@ -95,15 +96,6 @@ export function useMode() {
} }
} }
let direction
if (Math.abs(vector.x) > Math.abs(vector.y)) {
// x축 방향으로 더 많이 이동
direction = vector.x > 0 ? 'right' : 'left'
} else {
// y축 방향으로 더 많이 이동
direction = vector.y > 0 ? 'bottom' : 'top'
}
const line = new fabric.Line( const line = new fabric.Line(
[ [
points.current[0].left, points.current[0].left,
@ -115,10 +107,12 @@ export function useMode() {
stroke: 'black', stroke: 'black',
strokeWidth: 2, strokeWidth: 2,
selectable: false, selectable: false,
direction: direction, direction: getDirection(points.current[0], points.current[1]),
}, },
) )
historyLines.current.push(line)
console.log(line)
const text = new fabric.Text(length.toString(), { const text = new fabric.Text(length.toString(), {
left: left:
(points.current[0].left + (points.current[0].left +
@ -136,7 +130,7 @@ export function useMode() {
// 라인의 끝에 점을 추가합니다. // 라인의 끝에 점을 추가합니다.
const endPointCircle = new fabric.Circle({ const endPointCircle = new fabric.Circle({
radius: 5, radius: 1,
fill: 'transparent', // 원 안을 비웁니다. fill: 'transparent', // 원 안을 비웁니다.
stroke: 'black', // 원 테두리 색상을 검은색으로 설정합니다. stroke: 'black', // 원 테두리 색상을 검은색으로 설정합니다.
left: points.current[0].left + scaledVector.x, left: points.current[0].left + scaledVector.x,
@ -146,6 +140,8 @@ export function useMode() {
selectable: false, selectable: false,
}) })
console.log(endPointCircle)
canvas?.add(line) canvas?.add(line)
canvas?.add(text) canvas?.add(text)
canvas?.add(endPointCircle) canvas?.add(endPointCircle)
@ -163,31 +159,23 @@ export function useMode() {
}) })
} }
const templateMode = (canvas) => { const templateMode = () => {
changeMode(canvas, MODE.EDIT) changeMode(canvas, UseMode.EDIT)
if (historyPoints.current.length >= 4) { if (historyPoints.current.length >= 4) {
const firstPoint = historyPoints.current[0] const firstPoint = historyPoints.current[0]
const lastPoint = historyPoints.current[historyPoints.current.length - 1] const lastPoint = historyPoints.current[historyPoints.current.length - 1]
const line = new fabric.Line(
[firstPoint.left, firstPoint.top, lastPoint.left, lastPoint.top],
{
stroke: 'black',
strokeWidth: 2,
selectable: false,
},
)
historyPoints.current.forEach((point) => { historyPoints.current.forEach((point) => {
canvas?.remove(point) canvas?.remove(point)
}) })
drawLineWithLength(lastPoint, firstPoint)
points.current = []
historyPoints.current = [] historyPoints.current = []
canvas?.add(line) makePolygon()
canvas?.renderAll()
} }
} }
const textboxMode = (canvas) => { const textboxMode = () => {
canvas?.on('mouse:down', function (options) { canvas?.on('mouse:down', function (options) {
if (canvas?.getActiveObject()?.type === 'textbox') return if (canvas?.getActiveObject()?.type === 'textbox') return
const pointer = canvas?.getPointer(options.e) const pointer = canvas?.getPointer(options.e)
@ -204,12 +192,12 @@ export function useMode() {
canvas?.renderAll() canvas?.renderAll()
// textbox가 active가 풀린 경우 editing mode로 변경 // textbox가 active가 풀린 경우 editing mode로 변경
textbox?.on('editing:exited', function () { textbox?.on('editing:exited', function () {
changeMode(canvas, MODE.EDIT) changeMode(canvas, UseMode.EDIT)
}) })
}) })
} }
const drawLineMode = (canvas) => { const drawLineMode = () => {
canvas?.on('mouse:down', function (options) { canvas?.on('mouse:down', function (options) {
const pointer = canvas?.getPointer(options.e) const pointer = canvas?.getPointer(options.e)
@ -227,7 +215,7 @@ export function useMode() {
}) })
} }
const drawRectMode = (canvas) => { const drawRectMode = () => {
let rect, isDown, origX, origY let rect, isDown, origX, origY
canvas.on('mouse:down', function (o) { canvas.on('mouse:down', function (o) {
isDown = true isDown = true
@ -269,5 +257,80 @@ export function useMode() {
}) })
} }
return { mode, changeMode } /**
* 사이의 방향을 반환합니다.
*/
const getDirection = (a, b) => {
const vector = {
x: b.left - a.left,
y: b.top - a.top,
}
if (Math.abs(vector.x) > Math.abs(vector.y)) {
// x축 방향으로 더 많이 이동
return vector.x > 0 ? 'right' : 'left'
} else {
// y축 방향으로 더 많이 이동
return vector.y > 0 ? 'bottom' : 'top'
}
}
/**
* 점을 연결하는 선과 길이를 그립니다.
*/
const drawLineWithLength = (a, b) => {
const vector = {
x: b.left - a.left,
y: b.top - a.top,
}
const line = new fabric.Line([a.left, a.top, b.left, b.top], {
stroke: 'black',
strokeWidth: 2,
selectable: false,
direction: getDirection(a, b),
})
historyLines.current.push(line)
const text = new fabric.Text(
Math.round(Math.sqrt(vector.x ** 2 + vector.y ** 2)).toString(),
{
left: (a.left + b.left) / 2,
top: (a.top + b.top) / 2,
fontSize: 15,
originX: 'center',
originY: 'center',
selectable: false,
},
)
canvas?.add(line)
canvas?.add(text)
canvas?.renderAll()
}
const makePolygon = () => {
// 캔버스에서 모든 라인 객체를 찾습니다.
const lines = historyLines.current
// 각 라인의 시작점과 끝점을 사용하여 다각형의 점 배열을 생성합니다.
const points = lines.map((line) => ({ x: line.x1, y: line.y1 }))
// 모든 라인 객체를 캔버스에서 제거합니다.
lines.forEach((line) => canvas.remove(line))
// 점 배열을 사용하여 새로운 다각형 객체를 생성합니다.
const polygon = new fabric.Polygon(points, {
stroke: 'black',
fill: 'transparent',
selectable: false,
})
// 새로운 다각형 객체를 캔버스에 추가합니다.
canvas.add(polygon)
// 캔버스를 다시 그립니다.
canvas.renderAll()
}
return { mode, changeMode, setCanvas }
} }