mode 관련 추가

This commit is contained in:
hyojun.choi 2024-06-24 15:52:57 +09:00
parent f28b183c01
commit b2821c6081
2 changed files with 193 additions and 119 deletions

171
src/app/mode.js Normal file
View File

@ -0,0 +1,171 @@
import { useRef, useState } from 'react'
export function useMode() {
const [mode, setMode] = useState(MODE.EDIT)
const points = useRef([])
const addEvent = (canvas, mode) => {
switch (mode) {
case 'edit':
editMode(canvas)
break
case 'template':
templateMode(canvas)
break
case 'textbox':
textboxMode(canvas)
break
}
}
const changeMode = (canvas, mode) => {
setMode(mode)
// mode변경 시 이전 이벤트 제거
canvas?.off('mouse:down')
addEvent(canvas, mode)
}
const editMode = (canvas) => {
canvas?.on('mouse:down', function (options) {
const pointer = canvas?.getPointer(options.e)
const circle = new fabric.Circle({
radius: 5,
fill: 'transparent', // 원 안을 비웁니다.
stroke: 'black', // 원 테두리 색상을 검은색으로 설정합니다.
left: pointer.x,
top: pointer.y,
originX: 'center',
originY: 'center',
selectable: false,
})
points.current.push(circle)
canvas?.add(circle)
if (points.current.length === 2) {
const length = Number(prompt('길이를 입력하세요:'))
// length 값이 숫자가 아닌 경우
if (isNaN(length) || length === 0) {
// 기존에 추가된 circle과 pointer를 제거합니다.
points.current.forEach((point) => {
canvas?.remove(point)
})
points.current = []
return
}
if (length) {
const vector = {
x: points.current[1].left - points.current[0].left,
y: points.current[1].top - points.current[0].top,
}
const slope = Math.abs(vector.y / vector.x) // 기울기 계산
let scaledVector
if (slope >= 1) {
// 기울기가 1 이상이면 x축 방향으로 그림
scaledVector = {
x: 0,
y: vector.y >= 0 ? Number(length) : -Number(length),
}
} else {
// 기울기가 1 미만이면 y축 방향으로 그림
scaledVector = {
x: vector.x >= 0 ? Number(length) : -Number(length),
y: 0,
}
}
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(
[
points.current[0].left,
points.current[0].top,
points.current[0].left + scaledVector.x,
points.current[0].top + scaledVector.y,
],
{
stroke: 'black',
strokeWidth: 2,
selectable: false,
direction: direction,
},
)
const text = new fabric.Text(length.toString(), {
left:
(points.current[0].left +
points.current[0].left +
scaledVector.x) /
2,
top:
(points.current[0].top + points.current[0].top + scaledVector.y) /
2,
fontSize: 15,
originX: 'center',
originY: 'center',
selectable: false,
})
// 라인의 끝에 점을 추가합니다.
const endPointCircle = new fabric.Circle({
radius: 5,
fill: 'transparent', // 원 안을 비웁니다.
stroke: 'black', // 원 테두리 색상을 검은색으로 설정합니다.
left: points.current[0].left + scaledVector.x,
top: points.current[0].top + scaledVector.y,
originX: 'center',
originY: 'center',
selectable: false,
})
canvas?.add(line)
canvas?.add(text)
canvas?.add(endPointCircle)
points.current.forEach((point) => {
canvas?.remove(point)
})
points.current = [endPointCircle]
}
}
canvas?.renderAll()
})
}
const templateMode = (canvas) => {}
const textboxMode = (canvas) => {
canvas?.on('mouse:down', function (options) {
const pointer = canvas?.getPointer(options.e)
const textbox = new fabric.Textbox('텍스트를 입력하세요', {
left: pointer.x,
top: pointer.y,
width: 150, // 텍스트박스의 너비를 설정합니다.
fontSize: 16, // 텍스트의 크기를 설정합니다.
})
canvas?.add(textbox)
canvas?.setActiveObject(textbox) // 생성된 텍스트박스를 활성 객체로 설정합니다.
canvas?.renderAll()
})
}
return { mode, changeMode }
}
export const MODE = {
EDIT: 'edit',
TEMPLATE: 'template',
TEXTBOX: 'textbox',
}

View File

@ -1,135 +1,38 @@
import { useCanvas } from '@/hooks/useCanvas'
import { useEffect, useRef } from 'react'
import { useEffect } from 'react'
import { MODE, useMode } from '@/app/mode'
export default function Roof2() {
const { canvas } = useCanvas('canvas')
const mode = useRef('')
const points = useRef([])
const { mode, changeMode } = useMode()
useEffect(() => {
canvas?.on('mouse:down', function (options) {
const pointer = canvas?.getPointer(options.e)
const circle = new fabric.Circle({
radius: 5,
fill: 'transparent', // .
stroke: 'black', // .
left: pointer.x,
top: pointer.y,
originX: 'center',
originY: 'center',
selectable: false,
})
// canvas
if (!canvas) return
// canvas
changeMode(canvas, mode)
}, [mode])
points.current.push(circle)
canvas?.add(circle)
if (points.current.length === 2) {
const length = Number(prompt('길이를 입력하세요:'))
// length
if (isNaN(length) || length === 0) {
// circle pointer .
points.current.forEach((point) => {
canvas?.remove(point)
})
points.current = []
return
}
if (length) {
const vector = {
x: points.current[1].left - points.current[0].left,
y: points.current[1].top - points.current[0].top,
}
const slope = Math.abs(vector.y / vector.x) //
let scaledVector
if (slope >= 1) {
// 1 x
scaledVector = {
x: 0,
y: vector.y >= 0 ? Number(length) : -Number(length),
}
} else {
// 1 y
scaledVector = {
x: vector.x >= 0 ? Number(length) : -Number(length),
y: 0,
}
}
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(
[
points.current[0].left,
points.current[0].top,
points.current[0].left + scaledVector.x,
points.current[0].top + scaledVector.y,
],
{
stroke: 'black',
strokeWidth: 2,
selectable: false,
direction: direction,
},
)
const text = new fabric.Text(length.toString(), {
left:
(points.current[0].left +
points.current[0].left +
scaledVector.x) /
2,
top:
(points.current[0].top + points.current[0].top + scaledVector.y) /
2,
fontSize: 15,
originX: 'center',
originY: 'center',
selectable: false,
})
// .
const endPointCircle = new fabric.Circle({
radius: 5,
fill: 'transparent', // .
stroke: 'black', // .
left: points.current[0].left + scaledVector.x,
top: points.current[0].top + scaledVector.y,
originX: 'center',
originY: 'center',
selectable: false,
})
canvas?.add(line)
canvas?.add(text)
canvas?.add(endPointCircle)
points.current.forEach((point) => {
canvas?.remove(point)
})
points.current = [endPointCircle]
}
}
canvas?.renderAll()
})
}, [canvas])
return (
<>
<div className="flex justify-center my-8">
<button className="w-30 mx-2 p-2 rounded bg-blue-500 text-white">
<button
className={`w-30 mx-2 p-2 rounded ${mode === MODE.EDIT ? 'bg-blue-500' : 'bg-gray-500'} text-white`}
onClick={() => changeMode(canvas, MODE.EDIT)}
>
Editing
</button>
<button className="w-30 mx-2 p-2 rounded bg-gray-500 text-white">
<button
className={`w-30 mx-2 p-2 rounded ${mode === MODE.TEMPLATE ? 'bg-blue-500' : 'bg-gray-500'} text-white`}
onClick={() => changeMode(canvas, MODE.TEMPLATE)}
>
Template
</button>
<button className="w-30 mx-2 p-2 rounded bg-gray-500 text-white">
<button
className={`w-30 mx-2 p-2 rounded ${mode === MODE.TEXTBOX ? 'bg-blue-500' : 'bg-gray-500'} text-white`}
onClick={() => changeMode(canvas, MODE.TEXTBOX)}
>
텍스트박스 mode
</button>
</div>