diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index 108303b3..eba3000e 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -7,11 +7,11 @@ import QPolygon from '@/components/fabric/QPolygon' import RangeSlider from './ui/RangeSlider' import { useRecoilState, useRecoilValue } from 'recoil' -import { canvasSizeState, fontSizeState, sortedPolygonArray } from '@/store/canvasAtom' +import { canvasListState, canvasSizeState, fontSizeState, sortedPolygonArray } from '@/store/canvasAtom' import { QLine } from '@/components/fabric/QLine' export default function Roof2() { - const { canvas, handleRedo, handleUndo, setCanvasBackgroundWithDots, saveImage } = useCanvas('canvas') + const { canvas, handleRedo, handleUndo, setCanvasBackgroundWithDots, saveImage, addCanvas, changeCanvas } = useCanvas('canvas') //canvas 기본 사이즈 const [canvasSize, setCanvasSize] = useRecoilState(canvasSizeState) @@ -29,6 +29,9 @@ export default function Roof2() { const [showControl, setShowControl] = useState(false) + const [canvasIdx, setCanvasIdx] = useState(0) + const canvasList = useRecoilValue(canvasListState) + const { mode, changeMode, handleClear, fillCellInPolygon, zoomIn, zoomOut, zoom, togglePolygonLine, handleOuterlinesTest2, applyTemplateB } = useMode() @@ -180,6 +183,8 @@ export default function Roof2() { canvas?.add(polygon) + console.log(polygon) + handleOuterlinesTest2(polygon) // const lines = togglePolygonLine(polygon) @@ -200,17 +205,19 @@ export default function Roof2() { const makeQLine = () => { if (canvas) { - const line = new QLine( - [50, 50, 200, 50], + const line = new fabric.QLine( + [200, 200, 500, 500], { stroke: 'black', strokeWidth: 1, fontSize: fontSize, + selectable: true, }, 50, ) canvas?.add(line) + console.log(line) } } @@ -240,6 +247,12 @@ export default function Roof2() { setShowControl(!showControl) } + const nextCanvas = () => { + const nextCanvasNumber = canvasIdx < canvasList.length - 1 ? canvasIdx + 1 : 0 + setCanvasIdx(nextCanvasNumber) + changeCanvas(nextCanvasNumber) + } + return ( <> {canvas && ( @@ -333,6 +346,12 @@ export default function Roof2() { + + diff --git a/src/components/fabric/QLine.js b/src/components/fabric/QLine.js index 7796e66e..6a3ce67c 100644 --- a/src/components/fabric/QLine.js +++ b/src/components/fabric/QLine.js @@ -16,7 +16,7 @@ export class QLine extends fabric.Group { isAlreadyInterSection = false interSectionPoints = [] - #lengthTxt = 0 + lengthTxt = 0 constructor(points, option = { isActiveLengthText: true }, lengthTxt) { // 소수점 전부 제거 @@ -44,24 +44,24 @@ export class QLine extends fabric.Group { this.idx = option.idx if (lengthTxt > 0) { - this.#lengthTxt = Number(lengthTxt) + this.lengthTxt = Number(lengthTxt) } - option.isActiveLengthText ?? this.#init() - this.#addControl() + option.isActiveLengthText ?? this.init() + this.addControl() } - #init() { - this.#addLengthText(true) + init() { + this.addLengthText(true) } - #addControl() { + addControl() { this.on('moving', () => { - this.#addLengthText(false) + this.addLengthText(false) }) this.on('modified', (e) => { - this.#addLengthText(false) + this.addLengthText(false) }) this.on('selected', () => { @@ -73,19 +73,19 @@ export class QLine extends fabric.Group { }) } - #addLengthText(isFirst) { + addLengthText(isFirst) { if (this.text) { this.removeWithUpdate(this.text) this.text = null } - if (isFirst && this.#lengthTxt > 0) { - const text = new fabric.Textbox(this.#lengthTxt.toFixed(0).toString(), { + if (isFirst && this.lengthTxt > 0) { + const text = new fabric.Textbox(this.lengthTxt.toFixed(0).toString(), { left: (this.x1 + this.x2) / 2, top: (this.y1 + this.y2) / 2, fontSize: this.fontSize, }) - this.length = this.#lengthTxt + this.length = this.lengthTxt this.text = text this.addWithUpdate(text) return @@ -115,4 +115,37 @@ export class QLine extends fabric.Group { this.text.set({ fontSize }) this.addWithUpdate() } + + fromObject(object, callback) { + fabric.util.enlivenObjects(object.objects, function (enlivenedObjects) { + delete object.objects + callback && callback(new fabric.Frindle(enlivenedObjects, object)) + }) + } + + async = true + + toObject(propertiesToInclude) { + return fabric.util.object.extend(this.callSuper('toObject'), { + length: this.length, + line: this.line, + text: this.text, + fontSize: this.fontSize, + x1: this.x1, + y1: this.y1, + x2: this.x2, + y2: this.y2, + direction: this.direction, + idx: this.idx, + type: this.type, + parent: this.parent, + isAlreadyInterSection: this.isAlreadyInterSection, + interSectionPoints: this.interSectionPoints, + lengthTxt: this.lengthTxt, + setFontSize: this.setFontSize, + addLengthText: this.addLengthText, + init: this.init, + addControl: this.addControl, + }) + } } diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index f7bc2509..75e62a19 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -28,6 +28,7 @@ export default class QPolygon extends fabric.Group { helpLines = [] wall + constructor(points, options, canvas) { /*if (points.length !== 4 && points.length !== 6) { throw new Error('Points must be 4 or 6.') @@ -51,13 +52,13 @@ export default class QPolygon extends fabric.Group { this.points = sortPoints this.polygon = polygon this.name = options.name - this.#init() - this.#addEvent() - this.#initLines() + this.init() + this.addEvent() + this.initLines() this.setShape() } - #initLines() { + initLines() { this.points.forEach((point, i) => { const nextPoint = this.points[(i + 1) % this.points.length] const line = new QLine([point.x, point.y, nextPoint.x, nextPoint.y], { @@ -71,13 +72,13 @@ export default class QPolygon extends fabric.Group { }) } - #init() { - this.#addLengthText() + init() { + this.addLengthText() } - #addEvent() { + addEvent() { this.on('scaling', (e) => { - this.#updateLengthText() + this.updateLengthText() }) this.on('selected', function () { @@ -110,7 +111,7 @@ export default class QPolygon extends fabric.Group { this.addWithUpdate() } - #addLengthText() { + addLengthText() { if (this.texts.length > 0) { this.texts.forEach((text) => { this.canvas.remove(text) @@ -142,7 +143,7 @@ export default class QPolygon extends fabric.Group { this.canvas.renderAll() } - #updateLengthText() { + updateLengthText() { const points = this.getCurrentPoints() points.forEach((start, i) => { @@ -188,7 +189,7 @@ export default class QPolygon extends fabric.Group { new fabric.Point(rect.left + rect.width, rect.top + rect.height), ] - const isInside = rectPoints.every((rectPoint) => this.inPolygon(rectPoint) && this.#distanceFromEdge(rectPoint) >= cell.padding) + const isInside = rectPoints.every((rectPoint) => this.inPolygon(rectPoint) && this.distanceFromEdge(rectPoint) >= cell.padding) if (isInside) { this.addWithUpdate(rect) @@ -245,7 +246,7 @@ export default class QPolygon extends fabric.Group { return intersects % 2 === 1 } - #distanceFromEdge(point) { + distanceFromEdge(point) { const vertices = this.getCurrentPoints() let minDistance = Infinity @@ -317,13 +318,13 @@ export default class QPolygon extends fabric.Group { } if (this.lines.length === 4) { - this.#drawHelpLineInRect(chon) + this.drawHelpLineInRect(chon) } else if (this.lines.length === 6 || this.lines.length === 8) { // TODO : 6각형 - this.#drawHelpLineInHexagon2(chon) + this.drawHelpLineInHexagon2(chon) } /* else if (this.lines.length === 8) { // TODO : 8각형 - this.#drawHelpLineInOctagon(chon) + this.drawHelpLineInOctagon(chon) }*/ } @@ -370,7 +371,7 @@ export default class QPolygon extends fabric.Group { return this.shape } - #drawHelpLineInRect(chon) { + drawHelpLineInRect(chon) { let type = 1 let smallestLength = Infinity let maxLength = 0 @@ -526,7 +527,7 @@ export default class QPolygon extends fabric.Group { this.canvas.add(ridge) } } - #drawHelpLineInHexagon2(chon) { + drawHelpLineInHexagon2(chon) { const oneSideLines = [...this.lines].map((line) => { let newX1, newY1, newX2, newY2 if (line.direction === 'top') { @@ -742,7 +743,7 @@ export default class QPolygon extends fabric.Group { this.canvas.renderAll() }) } - #drawHelpLineInHexagon(chon) { + drawHelpLineInHexagon(chon) { const historyLines = [] const helpPoints = [] const notInterSectionLines = [] @@ -884,5 +885,9 @@ export default class QPolygon extends fabric.Group { this.canvas.renderAll() } - #drawHelpLineInOctagon(chon) {} + drawHelpLineInOctagon(chon) {} + + getObject() { + return this + } } diff --git a/src/hooks/useCanvas.js b/src/hooks/useCanvas.js index 5f44060a..54e9609d 100644 --- a/src/hooks/useCanvas.js +++ b/src/hooks/useCanvas.js @@ -1,13 +1,9 @@ import { useEffect, useRef, useState } from 'react' import { fabric } from 'fabric' -import { - actionHandler, - anchorWrapper, - polygonPositionHandler, -} from '@/util/canvas-util' +import { actionHandler, anchorWrapper, getDirectionByPoint, polygonPositionHandler, sortedPoints } from '@/util/canvas-util' import { useRecoilState } from 'recoil' -import { canvasSizeState, fontSizeState } from '@/store/canvasAtom' +import { canvasListState, canvasSizeState, fontSizeState } from '@/store/canvasAtom' import QPolygon from '@/components/fabric/QPolygon' import { QLine } from '@/components/fabric/QLine' import QRect from '@/components/fabric/QRect' @@ -20,6 +16,8 @@ export function useCanvas(id) { const [fontSize] = useRecoilState(fontSizeState) const points = useRef([]) + const [canvasList, setCanvasList] = useRecoilState(canvasListState) + /** * 처음 셋팅 */ @@ -31,17 +29,6 @@ export function useCanvas(id) { selection: false, }) - // settings for all canvas in the app - fabric.Object.prototype.transparentCorners = false - fabric.Object.prototype.cornerColor = '#2BEBC8' - fabric.Object.prototype.cornerStyle = 'rect' - fabric.Object.prototype.cornerStrokeColor = '#2BEBC8' - fabric.Object.prototype.cornerSize = 6 - - QPolygon.prototype.canvas = c - QLine.prototype.canvas = c - QRect.prototype.canvas = c - setCanvas(c) return () => { c.dispose() @@ -57,24 +44,14 @@ export function useCanvas(id) { useEffect(() => { canvas ?.getObjects() - .filter( - (obj) => - obj.type === 'textbox' || - obj.type === 'text' || - obj.type === 'i-text', - ) + .filter((obj) => obj.type === 'textbox' || obj.type === 'text' || obj.type === 'i-text') .forEach((obj) => { obj.set({ fontSize: fontSize }) }) canvas ?.getObjects() - .filter( - (obj) => - obj.type === 'QLine' || - obj.type === 'QPolygon' || - obj.type === 'QRect', - ) + .filter((obj) => obj.type === 'QLine' || obj.type === 'QPolygon' || obj.type === 'QRect') .forEach((obj) => { obj.setFontSize(fontSize) }) @@ -121,26 +98,26 @@ export function useCanvas(id) { */ const removeMouseLines = () => { if (canvas?._objects.length > 0) { - const mouseLines = canvas?._objects.filter( - (obj) => obj.name === 'mouseLine', - ) + const mouseLines = canvas?._objects.filter((obj) => obj.name === 'mouseLine') mouseLines.forEach((item) => canvas?.remove(item)) } canvas?.renderAll() } - /** - * 눈금 그리기 - */ const initialize = () => { canvas?.clear() + // settings for all canvas in the app + fabric.Object.prototype.transparentCorners = false + fabric.Object.prototype.cornerColor = '#2BEBC8' + fabric.Object.prototype.cornerStyle = 'rect' + fabric.Object.prototype.cornerStrokeColor = '#2BEBC8' + fabric.Object.prototype.cornerSize = 6 - // 기존 이벤트가 있을 경우 제거한다. - // removeEventOnCanvas() + QPolygon.prototype.canvas = canvas + QLine.prototype.canvas = canvas + QRect.prototype.canvas = canvas - // 작업 후에 event를 추가해준다. - - // addEventOnCanvas() + fabric.QLine = QLine } /** @@ -176,26 +153,20 @@ export function useCanvas(id) { } // 가로선을 그립니다. - const horizontalLine = new fabric.Line( - [0, pointer.y, canvasSize.horizontal, pointer.y], - { - stroke: 'black', - strokeWidth: 1, - selectable: false, - name: 'mouseLine', - }, - ) + const horizontalLine = new fabric.Line([0, pointer.y, canvasSize.horizontal, pointer.y], { + stroke: 'black', + strokeWidth: 1, + selectable: false, + name: 'mouseLine', + }) // 세로선을 그립니다. - const verticalLine = new fabric.Line( - [pointer.x, 0, pointer.x, canvasSize.vertical], - { - stroke: 'black', - strokeWidth: 1, - selectable: false, - name: 'mouseLine', - }, - ) + const verticalLine = new fabric.Line([pointer.x, 0, pointer.x, canvasSize.vertical], { + stroke: 'black', + strokeWidth: 1, + selectable: false, + name: 'mouseLine', + }) // 선들을 캔버스에 추가합니다. canvas?.add(horizontalLine, verticalLine) @@ -325,7 +296,6 @@ export function useCanvas(id) { } const jsonStr = JSON.stringify(canvas) localStorage.setItem('canvas', jsonStr) - handleClear() } /** @@ -479,10 +449,7 @@ export function useCanvas(id) { poly.controls = poly.points.reduce(function (acc, point, index) { acc['p' + index] = new fabric.Control({ positionHandler: polygonPositionHandler, - actionHandler: anchorWrapper( - index > 0 ? index - 1 : lastControl, - actionHandler, - ), + actionHandler: anchorWrapper(index > 0 ? index - 1 : lastControl, actionHandler), actionName: 'modifyPolygon', pointIndex: index, }) @@ -571,6 +538,33 @@ export function useCanvas(id) { }) } + const addCanvas = () => { + const canvasState = canvas?.getObjects() + + canvasState.forEach((state) => { + console.log(state) + }) + + const str = JSON.stringify(canvasState) + + canvas?.clear() + + JSON.parse(str).forEach((state) => { + canvas?.add(state) + }) + + /*const stateArr = canvasState.map((state) => state.getObject().getObjects()) + + const newCanvasList = [...canvasList, JSON.stringify(stateArr)] + + setCanvasList(newCanvasList)*/ + } + + const changeCanvas = (idx) => { + canvas?.clear() + const canvasState = JSON.parse(canvasList[idx]) + } + return { canvas, addShape, @@ -585,5 +579,7 @@ export function useCanvas(id) { saveImage, handleFlip, setCanvasBackgroundWithDots, + addCanvas, + changeCanvas, } } diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index d1a60e04..c22e9c7b 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -718,8 +718,6 @@ export function useMode() { const handleOuterlinesTest = (polygon, offset = 71) => { var offsetPoints = [] - debugger - const sortedIndex = getStartIndex(polygon.lines) let tmpArraySorted = rearrangeArray(polygon.lines, sortedIndex) diff --git a/src/store/canvasAtom.js b/src/store/canvasAtom.js index 7d1d63fc..5a3c3d31 100644 --- a/src/store/canvasAtom.js +++ b/src/store/canvasAtom.js @@ -35,3 +35,9 @@ export const wallState = atom({ default: {}, dangerouslyAllowMutability: true, }) + +export const canvasListState = atom({ + key: 'canvas', + default: [], + dangerouslyAllowMutability: true, +})