diff --git a/src/components/floor-plan/CanvasMenu.jsx b/src/components/floor-plan/CanvasMenu.jsx index d8b7510c..320277fb 100644 --- a/src/components/floor-plan/CanvasMenu.jsx +++ b/src/components/floor-plan/CanvasMenu.jsx @@ -191,7 +191,12 @@ export default function CanvasMenu(props) {
- + {canvasZoom}%
diff --git a/src/components/floor-plan/modal/wallLineOffset/WallLineOffsetSetting.jsx b/src/components/floor-plan/modal/wallLineOffset/WallLineOffsetSetting.jsx index fcd48af1..a013934c 100644 --- a/src/components/floor-plan/modal/wallLineOffset/WallLineOffsetSetting.jsx +++ b/src/components/floor-plan/modal/wallLineOffset/WallLineOffsetSetting.jsx @@ -7,9 +7,20 @@ import { useWallLineOffsetSetting } from '@/hooks/roofcover/useWallLineOffsetSet export default function WallLineOffsetSetting({ setShowWallLineOffsetSettingModal }) { const { getMessage } = useMessage() - const { type, setType, buttonMenu, currentWallLineRef, TYPES, radioTypeRef, arrow1Ref, arrow2Ref, length1Ref, length2Ref, handleSave } = - useWallLineOffsetSetting() - const [buttonAct, setButtonAct] = useState(1) + const { + type, + setType, + buttonMenu, + currentWallLineRef, + TYPES, + radioTypeRef, + arrow1Ref, + arrow2Ref, + length1Ref, + length2Ref, + handleSave, + wallLineEditRef, + } = useWallLineOffsetSetting() const wallLineProps = { length1Ref, @@ -45,7 +56,7 @@ export default function WallLineOffsetSetting({ setShowWallLineOffsetSettingModa
{getMessage('setting')}
- {type === TYPES.WALL_LINE_EDIT && } + {type === TYPES.WALL_LINE_EDIT && } {type === TYPES.OFFSET && }
diff --git a/src/components/floor-plan/modal/wallLineOffset/type/WallLine.jsx b/src/components/floor-plan/modal/wallLineOffset/type/WallLine.jsx index 4f5c6ea7..15ccefa3 100644 --- a/src/components/floor-plan/modal/wallLineOffset/type/WallLine.jsx +++ b/src/components/floor-plan/modal/wallLineOffset/type/WallLine.jsx @@ -1,8 +1,8 @@ import { useMessage } from '@/hooks/useMessage' -import { useEffect, useState } from 'react' +import { forwardRef, useEffect, useImperativeHandle, useState } from 'react' import { useEvent } from '@/hooks/useEvent' -export default function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref, radioTypeRef, currentWallLineRef }) { +export default forwardRef(function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref, radioTypeRef, currentWallLineRef }, ref) { const { getMessage } = useMessage() const { addDocumentEventListener, initEvent } = useEvent() const [type, setType] = useState(1) @@ -10,76 +10,25 @@ export default function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref, const [arrow2, setArrow2] = useState('up') useEffect(() => { - addDocumentEventListener('keydown', document, keyDown) - return () => { initEvent() } }, []) + useImperativeHandle(ref, () => ({ + setArrow, + })) + + const setArrow = () => { + setArrow1(arrow1Ref.current) + setArrow2(arrow2Ref.current) + } + const onChange = (e) => { setType(Number(e.target.value)) radioTypeRef.current = e.target.value } - const handleBtnClick = (direction) => { - document.dispatchEvent(new KeyboardEvent('keydown', { key: direction })) - } - - const keyDown = (e) => { - if (currentWallLineRef.current === null) { - alert('보조선을 먼저 선택하세요') - return - } - - const key = e.key - - switch (key) { - case 'Down': // IE/Edge에서 사용되는 값 - case 'ArrowDown': { - if (radioTypeRef.current === '1') { - setArrow1('down') - arrow1Ref.current = 'down' - } else { - setArrow2('down') - arrow2Ref.current = 'down' - } - - break - } - case 'Up': // IE/Edge에서 사용되는 값 - case 'ArrowUp': - if (radioTypeRef.current === '1') { - setArrow1('up') - arrow1Ref.current = 'up' - } else { - setArrow2('up') - arrow2Ref.current = 'up' - } - break - case 'Left': // IE/Edge에서 사용되는 값 - case 'ArrowLeft': - if (radioTypeRef.current === '1') { - setArrow1('left') - arrow1Ref.current = 'left' - } else { - setArrow2('left') - arrow2Ref.current = 'left' - } - break - case 'Right': // IE/Edge에서 사용되는 값 - case 'ArrowRight': - if (radioTypeRef.current === '1') { - setArrow1('right') - arrow1Ref.current = 'right' - } else { - setArrow2('right') - arrow2Ref.current = 'right' - } - break - } - } - return ( <>
@@ -107,10 +56,10 @@ export default function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref,
- - - - + + + +
@@ -141,10 +90,10 @@ export default function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref,
- - - - + + + +
@@ -155,4 +104,4 @@ export default function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref,
) -} +}) diff --git a/src/hooks/roofcover/useWallLineOffsetSetting.js b/src/hooks/roofcover/useWallLineOffsetSetting.js index 6536bc80..dce62b9b 100644 --- a/src/hooks/roofcover/useWallLineOffsetSetting.js +++ b/src/hooks/roofcover/useWallLineOffsetSetting.js @@ -10,6 +10,7 @@ export function useWallLineOffsetSetting() { const { showLine, addLine } = useLine() const { getMessage } = useMessage() const { addCanvasMouseEventListener, initEvent } = useEvent() + const wallLineEditRef = useRef(null) const length1Ref = useRef(null) const length2Ref = useRef(null) const radioTypeRef = useRef('1') @@ -17,6 +18,22 @@ export function useWallLineOffsetSetting() { const arrow1Ref = useRef(null) const arrow2Ref = useRef(null) + const drawLine = (point1, point2, idx) => { + const line = addLine([point1.x, point1.y, point2.x, point2.y], { + stroke: 'black', + strokeWidth: 4, + idx: idx, + selectable: true, + name: 'outerLine', + x1: point1.x, + y1: point1.y, + x2: point2.x, + y2: point2.y, + }) + + line.attributes = { ...currentWallLineRef.current.attributes } + } + const TYPES = { WALL_LINE_EDIT: 'wallLineEdit', OFFSET: 'offset', @@ -36,37 +53,114 @@ export function useWallLineOffsetSetting() { showLine(outerLine) }) - const roofs = canvas.getObjects().filter((obj) => obj.name === 'roofBase') - - roofs.forEach((roof) => { - roof.innerLines.forEach((innerLine) => { - canvas.remove(innerLine) - }) - canvas.remove(roof) + //outerLine과 그 text만 남겨둔다. + const exceptObjs = canvas.getObjects().filter((obj) => obj.name !== 'outerLine' && obj.parent?.name !== 'outerLine') + exceptObjs.forEach((obj) => { + canvas.remove(obj) }) - const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine') - wallLines.forEach((wallLine) => { - canvas.remove(wallLine) - }) + canvas.setActiveObject(outerLines[0]) + currentWallLineRef.current = outerLines[0] + addCircleByLine(currentWallLineRef.current) addCanvasMouseEventListener('mouse:down', mouseDown) return () => { + removeOuterLineEditCircle() canvas.discardActiveObject() initEvent() } }, []) + useEffect(() => { + removeOuterLineEditCircle() + addCanvasMouseEventListener('mouse:down', mouseDown) + if (type === TYPES.WALL_LINE_EDIT) { + addCircleByLine(currentWallLineRef.current) + } + }, [type]) + + const removeOuterLineEditCircle = () => { + canvas.remove(...canvas.getObjects().filter((obj) => obj.name === 'outerLineEditCircleStart' || obj.name === 'outerLineEditCircleEnd')) + } + const mouseDown = (e) => { + removeOuterLineEditCircle() if (!e.target || (e.target && e.target.name !== 'outerLine')) { + currentWallLineRef.current = null return } currentWallLineRef.current = e.target + if (type === TYPES.WALL_LINE_EDIT) { + addCircleByLine(currentWallLineRef.current) + } + } + + const addCircleByLine = (line) => { + const direction = currentWallLineRef.current?.direction + let startPoint + let endPoint + let circle1, circle2 + + if (direction === 'top' || direction === 'bottom') { + // y1, y2중에 더 작은 값이 startPoint, 더 큰 값이 endPoint + if (line.y1 > line.y2) { + startPoint = { x: line.x2, y: line.y2 } + endPoint = { x: line.x1, y: line.y1 } + } else { + startPoint = { x: line.x1, y: line.y1 } + endPoint = { x: line.x2, y: line.y2 } + } + arrow1Ref.current = 'down' + arrow2Ref.current = 'up' + } else { + // x1, x2중에 더 작은 값이 startPoint, 더 큰 값이 endPoint + if (line.x1 > line.x2) { + startPoint = { x: line.x2, y: line.y2 } + endPoint = { x: line.x1, y: line.y1 } + } else { + startPoint = { x: line.x1, y: line.y1 } + endPoint = { x: line.x2, y: line.y2 } + } + arrow1Ref.current = 'right' + arrow2Ref.current = 'left' + } + + circle1 = new fabric.Circle({ + radius: 10, + fill: 'red', + top: startPoint.y - 10, + left: startPoint.x - 10, + name: 'outerLineEditCircleStart', + hasControls: false, + hasBorders: false, + lockMovementX: true, + lockMovementY: true, + }) + + circle2 = new fabric.Circle({ + radius: 10, + fill: 'blue', + top: endPoint.y - 10, + left: endPoint.x - 10, + name: 'outerLineEditCircleEnd', + hasControls: false, + hasBorders: false, + lockMovementX: true, + lockMovementY: true, + }) + + wallLineEditRef.current.setArrow() + canvas.add(circle1) + canvas.add(circle2) } const handleSave = () => { + if (currentWallLineRef.current === null) { + alert('보조선을 먼저 선택하세요') + return + } switch (type) { case TYPES.WALL_LINE_EDIT: handleWallLineEditSave() @@ -78,19 +172,49 @@ export function useWallLineOffsetSetting() { } const handleWallLineEditSave = () => { - const direction = currentWallLineRef.current.direction - let canDirections = direction === 'left' || direction === 'right' ? ['up', 'down'] : ['left', 'right'] - if (radioTypeRef === 1) { - if (!canDirections.includes(arrow1Ref.current)) { - alert('방향을 다시 선택하세요') - return + const startPoint = canvas + .getObjects() + .filter((obj) => obj.name === 'outerLineEditCircleStart') + .map((obj) => { + return { + x: obj.left + obj.radius, + y: obj.top + obj.radius, + } + })[0] + const endPoint = canvas + .getObjects() + .filter((obj) => obj.name === 'outerLineEditCircleEnd') + .map((obj) => { + return { + x: obj.left + obj.radius, + y: obj.top + obj.radius, + } + })[0] + const length = Number(length1Ref.current.value) / 10 + let line1, line2 + if (radioTypeRef.current === '1') { + // 1지점 선택시 방향은 down, right만 가능 + if (arrow1Ref.current === 'down') { + drawLine({ x: startPoint.x, y: startPoint.y }, { x: startPoint.x, y: startPoint.y + length }, currentWallLineRef.current.idx) + drawLine({ x: startPoint.x, y: startPoint.y + length }, { x: endPoint.x, y: endPoint.y }, currentWallLineRef.current.idx) + } else { + drawLine({ x: startPoint.x, y: startPoint.y }, { x: startPoint.x + length, y: startPoint.y }, currentWallLineRef.current.idx) + drawLine({ x: startPoint.x + length, y: startPoint.y }, { x: endPoint.x, y: endPoint.y }, currentWallLineRef.current.idx) } } else { - if (!canDirections.includes(arrow2Ref.current)) { - alert('방향을 다시 선택하세요') - return + // 2지점일 경우 방향은 up, left만 가능 + if (arrow2Ref.current === 'up') { + drawLine({ x: endPoint.x, y: endPoint.y }, { x: endPoint.x, y: endPoint.y - length }, currentWallLineRef.current.idx) + drawLine({ x: endPoint.x, y: endPoint.y - length }, { x: startPoint.x, y: startPoint.y }, currentWallLineRef.current.idx) + } else { + drawLine({ x: endPoint.x, y: endPoint.y }, { x: endPoint.x - length, y: endPoint.y }, currentWallLineRef.current.idx) + drawLine({ x: endPoint.x - length, y: endPoint.y }, { x: startPoint.x, y: startPoint.y }, currentWallLineRef.current.idx) } } + removeOuterLineEditCircle() + canvas.remove(currentWallLineRef.current) + currentWallLineRef.current = null + canvas.renderAll() } const handleOffsetSave = () => { @@ -103,26 +227,100 @@ export function useWallLineOffsetSetting() { } const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + const idx = currentWallLineRef.current.idx - const prevLine = outerLines.find((line) => (line.idx === idx - 1 < 0 ? outerLines.length - 1 : idx - 1)) + const prevIdx = idx - 1 <= 0 ? outerLines.length : idx - 1 + const nextIdx = idx + 1 > outerLines.length ? 1 : idx + 1 + const currentLine = currentWallLineRef.current - const nextLine = outerLines.find((line) => (line.idx === idx + 1 > outerLines.length - 1 ? 0 : idx + 1)) + const prevLine = outerLines.find((line) => line.idx === prevIdx) + const nextLine = outerLines.find((line) => line.idx === nextIdx) + + const length = length1Ref.current.value / 10 + switch (arrow1Ref.current) { case 'up': { - console.log(prevLine, currentLine, nextLine) + const currentLineY = Math.floor(Math.max(currentLine.y1, currentLine.y2)) + currentLine.set({ y1: currentLineY - length, y2: currentLineY - length }) + + if (Math.abs(currentLineY - prevLine.y1) < 2) { + prevLine.set({ y1: currentLineY - length }) + } else { + prevLine.set({ y2: currentLineY - length }) + } + if (Math.abs(currentLineY - nextLine.y1) < 2) { + nextLine.set({ y1: currentLineY - length }) + } else { + nextLine.set({ y2: currentLineY - length }) + } + break } case 'down': { + const currentLineY = Math.floor(Math.max(currentLine.y1, currentLine.y2)) + currentLine.set({ y1: currentLineY + length, y2: currentLineY + length }) + + if (Math.abs(currentLineY - prevLine.y1) < 2) { + prevLine.set({ y1: currentLineY + length }) + } else { + prevLine.set({ y2: currentLineY + length }) + } + if (Math.abs(currentLineY - nextLine.y1) < 2) { + nextLine.set({ y1: currentLineY + length }) + } else { + nextLine.set({ y2: currentLineY + length }) + } break } case 'left': { + const currentLineX = Math.floor(Math.max(currentLine.x1, currentLine.x2)) + currentLine.set({ x1: currentLineX - length, x2: currentLineX - length }) + + if (Math.abs(currentLineX - prevLine.x1) < 2) { + prevLine.set({ x1: currentLineX - length }) + } else { + prevLine.set({ x2: currentLineX - length }) + } + if (Math.abs(currentLineX - nextLine.x1) < 2) { + nextLine.set({ x1: currentLineX - length }) + } else { + nextLine.set({ x2: currentLineX - length }) + } break } case 'right': { + const currentLineX = Math.floor(Math.max(currentLine.x1, currentLine.x2)) + currentLine.set({ x1: currentLineX + length, x2: currentLineX + length }) + + if (Math.abs(currentLineX - prevLine.x1) < 2) { + prevLine.set({ x1: currentLineX + length }) + } else { + prevLine.set({ x2: currentLineX + length }) + } + if (Math.abs(currentLineX - nextLine.x1) < 2) { + nextLine.set({ x1: currentLineX + length }) + } else { + nextLine.set({ x2: currentLineX + length }) + } + break } } + canvas.renderAll() } - return { type, setType, buttonMenu, TYPES, length1Ref, length2Ref, radioTypeRef, currentWallLineRef, arrow1Ref, arrow2Ref, handleSave } + return { + type, + setType, + buttonMenu, + TYPES, + length1Ref, + length2Ref, + radioTypeRef, + currentWallLineRef, + arrow1Ref, + arrow2Ref, + handleSave, + wallLineEditRef, + } }