우선 숫자 더블클릭 시 숫자만 수정 가능

This commit is contained in:
hyojun.choi 2024-06-21 10:21:13 +09:00
parent 86522f67e1
commit 414fb47c75
3 changed files with 170 additions and 98 deletions

View File

@ -77,23 +77,31 @@ export function anchorWrapper(anchorIndex, fn) {
export const getDistance = (x1, y1, x2, y2) => { export const getDistance = (x1, y1, x2, y2) => {
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)) return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2))
} }
// 선의 길이를 계산하는 함수
export const calculateLineLength = (x1, y1, x2, y2) => { // polygon의 각 변에 해당 점과 점 사이의 거리를 나타내는 IText를 추가하는 함수
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)) export function addDistanceTextToPolygon(polygon) {
} const points = polygon.get('points')
const texts = []
// 선과 텍스트를 그룹으로 묶는 함수
export const createGroupWithLineAndText = (line, text) => { for (let i = 0; i < points.length; i++) {
return new fabric.Group([line, text]) const start = points[i]
} const end = points[(i + 1) % points.length] // 다음 점 (마지막 점의 경우 첫번째 점으로)
const distance = Math.sqrt(
export const calculateShapeLength = (shape) => { Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2),
// 도형의 원래 길이를 가져옵니다. ) // 두 점 사이의 거리 계산
const originalLength = shape.width
const text = new fabric.Textbox(distance.toFixed(2), {
// 도형의 scaleX 값을 가져옵니다. // 소수 둘째자리까지 표시
const scaleX = shape.scaleX left: (start.x + end.x) / 2, // 텍스트의 위치는 두 점의 중간
top: (start.y + end.y) / 2,
// 도형의 현재 길이를 계산합니다. fontSize: 20,
return originalLength * scaleX })
texts.push(text)
}
return new fabric.Group([polygon, ...texts], {
// polygon과 텍스트들을 그룹화
selectable: true,
})
} }

View File

@ -1,4 +1,4 @@
import { createGroupWithLineAndText, getDistance } from '@/app/util/canvas-util' import { addDistanceTextToPolygon, getDistance } from '@/app/util/canvas-util'
import { useCanvas } from '@/hooks/useCanvas' import { useCanvas } from '@/hooks/useCanvas'
import { fabric } from 'fabric' import { fabric } from 'fabric'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
@ -19,20 +19,86 @@ export default function Roof() {
attachCustomControlOnPolygon, attachCustomControlOnPolygon,
saveImage, saveImage,
handleFlip, handleFlip,
updateTextOnLineChange,
} = useCanvas('canvas') } = useCanvas('canvas')
useEffect(() => { useEffect(() => {
// IText let circle = new fabric.Circle({
const text = new fabric.IText('Hello', { 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, left: 100,
top: 100, top: 100,
fill: 'red', originX: 'center',
originY: 'center',
}) })
text.on('editing:entered', () => {
console.log('editing:entered') 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,
}) })
canvas?.add(text)
// 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]) }, [canvas])
const addRect = () => { const addRect = () => {
@ -107,7 +173,7 @@ export default function Roof() {
[ [
{ x: 100, y: 100 }, // { x: 100, y: 100 }, //
{ x: 500, y: 100 }, // { x: 500, y: 100 }, //
{ x: 750, y: 400 }, // { x: 750, y: 700 }, //
{ x: 250, y: 400 }, // { x: 250, y: 400 }, //
], ],
{ {
@ -119,86 +185,93 @@ export default function Roof() {
objectCaching: false, objectCaching: false,
}, },
) )
// attachCustomControlOnPolygon(trapezoid) attachCustomControlOnPolygon(trapezoid)
const group = addDistanceTextToPolygon(trapezoid) const group = addDistanceTextToPolygon(trapezoid)
addGroupClickEvent(group) addGroupClickEvent(group)
group.getObjects().forEach(function (object, index) {
if (object.type === 'i-text') {
addTextModifiedEvent(object, trapezoid, index)
}
})
canvas?.add(group) canvas?.add(group)
canvas?.renderAll() canvas?.renderAll()
} }
// group group object // group group object
function addGroupClickEvent(group) { function addGroupClickEvent(group) {
group.on('mousedown', function () { group.on('selected', (e) => {
const objects = group.getObjects() console.log(e)
canvas?.remove(group)
objects.forEach(function (object) {
canvas?.add(object)
})
canvas?.renderAll()
}) })
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
}
} }
// polygon IText
function addDistanceTextToPolygon(polygon) {
const points = polygon.get('points')
const texts = []
for (let i = 0; i < points.length; i++) {
const start = points[i]
const end = points[(i + 1) % points.length] // ( )
const distance = Math.sqrt(
Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2),
) //
const text = new fabric.IText(distance.toFixed(2), {
//
left: (start.x + end.x) / 2, //
top: (start.y + end.y) / 2,
fontSize: 10,
editable: true,
}) })
texts.push(text) 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,
})
return new fabric.Group([polygon, ...texts], { // hide group inside text
// polygon closestTextbox.visible = false
selectable: true, // 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 // IText polygon
function addTextModifiedEvent(text, polygon, index) { function addTextModifiedEvent(text, polygon, index) {
text.on('editing:entered', function () { text.on('editing:exited', function () {})
console.log(123)
const newLength = parseFloat(text.text)
const points = polygon.get('points')
const start = points[index]
const end = points[(index + 1) % points.length]
const vector = { x: end.x - start.x, y: end.y - start.y } // start end
const length = Math.sqrt(vector.x * vector.x + vector.y * vector.y) // ( )
const normalizedVector = { x: vector.x / length, y: vector.y / length } // ( 1)
const scaledVector = {
x: normalizedVector.x * newLength,
y: normalizedVector.y * newLength,
} //
// end
end.x = start.x + scaledVector.x
end.y = start.y + scaledVector.y
// polygon
const newGroup = addDistanceTextToPolygon(polygon)
addGroupClickEvent(newGroup)
canvas.add(newGroup)
canvas.renderAll()
})
} }
const randomColor = () => { const randomColor = () => {

View File

@ -3,7 +3,6 @@ import { fabric } from 'fabric'
import { import {
actionHandler, actionHandler,
anchorWrapper, anchorWrapper,
calculateShapeLength,
polygonPositionHandler, polygonPositionHandler,
} from '@/app/util/canvas-util' } from '@/app/util/canvas-util'
@ -495,13 +494,6 @@ export function useCanvas(id) {
canvas?.renderAll() canvas?.renderAll()
} }
// 선의 길이가 변경될 때마다 텍스트를 업데이트하는 함수
const updateTextOnLineChange = (group, text) => {
const length = calculateShapeLength(group)
text.set({ text: length.toString() })
canvas?.renderAll()
}
return { return {
canvas, canvas,
addShape, addShape,
@ -516,6 +508,5 @@ export function useCanvas(id) {
attachCustomControlOnPolygon, attachCustomControlOnPolygon,
saveImage, saveImage,
handleFlip, handleFlip,
updateTextOnLineChange,
} }
} }