nalpari 3d7d2ab0dc refactor: util 폴더 위치 수정
nextjs 폴더명 예약 문제로 util 폴더 경로 수정
2024-07-03 10:34:25 +09:00

392 lines
10 KiB
JavaScript

import { addDistanceTextToPolygon, getDistance } from '@/util/canvas-util'
import { useCanvas } from '@/hooks/useCanvas'
import { fabric } from 'fabric'
import { v4 as uuidv4 } from 'uuid'
import { useEffect } from 'react'
export default function Roof() {
const {
canvas,
addShape,
handleUndo,
handleRedo,
handleClear,
handleCopy,
handleDelete,
handleSave,
handlePaste,
handleRotate,
attachCustomControlOnPolygon,
saveImage,
handleFlip,
} = useCanvas('canvas')
useEffect(() => {
let circle = new fabric.Circle({
radius: 40,
fill: 'rgba(200, 0, 0, 0.3)',
originX: 'center',
originY: 'center',
})
let text = new fabric.Textbox('AJLoveChina', {
originX: 'center',
originY: 'center',
textAlign: 'center',
fontSize: 12,
})
let group = new fabric.Group([circle, text], {
left: 100,
top: 100,
originX: 'center',
originY: 'center',
})
group.on('mousedblclick', () => {
// textForEditing is temporary obj,
// and will be removed after editing
console.log(text.type)
let textForEditing = new fabric.Textbox(text.text, {
originX: 'center',
originY: 'center',
textAlign: text.textAlign,
fontSize: text.fontSize,
left: group.left,
top: group.top,
})
// hide group inside text
text.visible = false
// note important, text cannot be hidden without this
group.addWithUpdate()
textForEditing.visible = true
// do not give controls, do not allow move/resize/rotation on this
textForEditing.hasConstrols = false
// now add this temporary obj to canvas
canvas.add(textForEditing)
canvas.setActiveObject(textForEditing)
// make the cursor showing
textForEditing.enterEditing()
textForEditing.selectAll()
// editing:exited means you click outside of the textForEditing
textForEditing.on('editing:exited', () => {
let newVal = textForEditing.text
let oldVal = text.text
// then we check if text is changed
if (newVal !== oldVal) {
text.set({
text: newVal,
visible: true,
})
// comment before, you must call this
group.addWithUpdate()
// we do not need textForEditing anymore
textForEditing.visible = false
canvas?.remove(textForEditing)
// optional, buf for better user experience
canvas?.setActiveObject(group)
}
})
})
canvas?.add(group)
}, [canvas])
const addRect = () => {
const rect = new fabric.Rect({
height: 200,
width: 200,
top: 10,
left: 10,
opacity: 0.4,
fill: randomColor(),
stroke: 'red',
name: uuidv4(),
})
addShape(rect)
}
const addHorizontalLine = () => {
const { x1, y1, x2, y2 } = { x1: 20, y1: 100, x2: 220, y2: 100 }
/**
* 시작X,시작Y,도착X,도착Y 좌표
*/
const horizontalLine = new fabric.Line([x1, y1, x2, y2], {
name: uuidv4(),
stroke: 'red',
strokeWidth: 3,
selectable: true,
})
const text = new fabric.Text(getDistance(x1, y1, x2, y2).toString(), {
fontSize: 20,
left: (x2 - x1) / 2,
top: y1 - 20,
})
const group = new fabric.Group([horizontalLine, text], {
left: 20,
top: 20,
})
// addShape(horizontalLine)
addShape(group)
console.log(JSON.stringify(canvas))
}
const addVerticalLine = () => {
const verticalLine = new fabric.Line([10, 10, 10, 100], {
name: uuidv4(),
stroke: 'red',
strokeWidth: 3,
selectable: true,
})
addShape(verticalLine)
}
const addTriangle = () => {
const triangle = new fabric.Triangle({
name: uuidv4(),
top: 50,
left: 50,
width: 100,
stroke: randomColor(),
strokeWidth: 3,
})
addShape(triangle)
}
const addTrapezoid = () => {
const trapezoid = new fabric.Polygon(
[
{ x: 100, y: 100 }, // 좌상단
{ x: 500, y: 100 }, // 우상단
{ x: 750, y: 700 }, // 우하단
{ x: 250, y: 400 }, // 좌하단
],
{
name: uuidv4(),
stroke: 'red',
opacity: 0.4,
strokeWidth: 3,
selectable: true,
objectCaching: false,
},
)
attachCustomControlOnPolygon(trapezoid)
const group = addDistanceTextToPolygon(trapezoid)
addGroupClickEvent(group)
canvas?.add(group)
canvas?.renderAll()
}
// group에 클릭 이벤트를 추가하여 클릭 시 group을 제거하고 object들만 남기는 함수
function addGroupClickEvent(group) {
group.on('selected', (e) => {
console.log(e)
})
group.on('mousedblclick', (e) => {
// textForEditing is temporary obj,
// and will be removed after editing
const pointer = canvas?.getPointer(e.e) // 마우스 클릭 위치 가져오기
let minDistance = Infinity
let closestTextbox = null
const groupPoint = group.getCenterPoint()
group.getObjects().forEach(function (object) {
if (object.type === 'textbox') {
// 객체가 TextBox인지 확인
const objectCenter = object.getCenterPoint() // TextBox 객체의 중심점 가져오기
const dx = objectCenter.x + groupPoint.x - pointer.x
const dy = objectCenter.y + groupPoint.y - pointer.y
const distance = Math.sqrt(dx * dx + dy * dy) // 마우스 클릭 위치와 TextBox 객체 사이의 거리 계산
if (distance < minDistance) {
// 가장 짧은 거리를 가진 TextBox 객체 찾기
minDistance = distance
closestTextbox = object
}
}
})
let textForEditing = new fabric.Textbox(closestTextbox.text, {
originX: 'center',
originY: 'center',
textAlign: closestTextbox.textAlign,
fontSize: closestTextbox.fontSize,
left: closestTextbox.left + groupPoint.x,
top: closestTextbox.top + groupPoint.y,
})
// hide group inside text
closestTextbox.visible = false
// note important, text cannot be hidden without this
group.addWithUpdate()
textForEditing.visible = true
// do not give controls, do not allow move/resize/rotation on this
textForEditing.hasConstrols = false
// now add this temporary obj to canvas
canvas?.add(textForEditing)
canvas?.setActiveObject(textForEditing)
// make the cursor showing
textForEditing?.enterEditing()
textForEditing?.selectAll()
// editing:exited means you click outside of the textForEditing
textForEditing?.on('editing:exited', () => {
let newVal = textForEditing.text
// then we check if text is changed
closestTextbox.set({
text: newVal,
visible: true,
})
// comment before, you must call this
group.addWithUpdate()
// we do not need textForEditing anymore
textForEditing.visible = false
canvas?.remove(textForEditing)
// optional, buf for better user experience
canvas?.setActiveObject(group)
})
})
}
// IText를 수정할 때 해당 값을 길이로 갖는 다른 polygon을 생성하고 다시 그룹화하는 함수
function addTextModifiedEvent(text, polygon, index) {
text.on('editing:exited', function () {})
}
const randomColor = () => {
return '#' + Math.round(Math.random() * 0xffffff).toString(16)
}
return (
<>
<div className="flex justify-center my-8 w-full">
<button
className="w-30 mx-2 p-2 rounded bg-blue-500 text-white"
onClick={addRect}
>
ADD RECTANGLE
</button>
<button
className="w-30 mx-2 p-2 rounded bg-blue-500 text-white"
onClick={addHorizontalLine}
>
ADD HORIZONTAL LINE
</button>
<button
className="w-30 mx-2 p-2 rounded bg-blue-500 text-white"
onClick={addVerticalLine}
>
ADD VERTICALITY LINE
</button>
<button
className="w-30 mx-2 p-2 rounded bg-blue-500 text-white"
onClick={addTriangle}
>
ADD TRIANGLE
</button>
<button
className="w-30 mx-2 p-2 rounded bg-blue-500 text-white"
onClick={addTrapezoid}
>
ADD TRAPEZOID
</button>
<button
className="w-30 mx-2 p-2 rounded bg-black text-white"
onClick={handleCopy}
>
COPY shape
</button>
<button
className="w-30 mx-2 p-2 rounded bg-red-500 text-white"
onClick={handleDelete}
>
DELETE
</button>
<button
className="w-30 mx-2 p-2 rounded bg-red-500 text-white"
onClick={handleClear}
>
CLEAR
</button>
<button
className="w-30 mx-2 p-2 rounded bg-green-500 text-white"
onClick={handleUndo}
>
UNDO
</button>
<button
className="w-30 mx-2 p-2 rounded bg-green-300 text-white"
onClick={handleRedo}
>
REDO
</button>
<button
className="w-30 mx-2 p-2 rounded bg-black text-white"
onClick={handleSave}
>
저장
</button>
<button
className="w-30 mx-2 p-2 rounded bg-black text-white"
onClick={handlePaste}
>
붙여넣기
</button>
<button
className="w-30 mx-2 p-2 rounded bg-black text-white"
onClick={() => handleRotate()}
>
45 회전
</button>
<button
className="w-30 mx-2 p-2 rounded bg-black text-white"
onClick={() => {
saveImage('제목')
}}
>
이미지 저장
</button>
<button
className="w-30 mx-2 p-2 rounded bg-black text-white"
onClick={handleFlip}
>
도형반전
</button>
</div>
<div
className="flex justify-center"
style={{
border: '1px solid',
width: 1000,
height: 1000,
margin: 'auto',
}}
>
<canvas id="canvas" />
</div>
</>
)
}