diff --git a/src/canvas/main.js b/src/canvas/main.js index 6fa06dd..56f1abe 100644 --- a/src/canvas/main.js +++ b/src/canvas/main.js @@ -20,7 +20,7 @@ function Canvas(){ useEffect(() => { return () => { - canvas?.dispose() // if already exist, dispose before re-initializing + canvas?.dispose() // canvas.dispose is async, https://github.com/fabricjs/fabric.js/issues/8299 } }, []) diff --git a/src/canvas/mainClass.js b/src/canvas/mainClass.js index 425f5ea..cc68e2f 100644 --- a/src/canvas/mainClass.js +++ b/src/canvas/mainClass.js @@ -1,12 +1,15 @@ import React from "react" import * as fabric from 'fabric' +import { FullscreenOutlined } from "@ant-design/icons" +import { Button, Tooltip } from "antd" class Canvas extends React.Component { constructor(props) { super(props) - + + /** @type {fabric.Canvas | null} */ this.fabricCanvas = null this.canvasRef = React.createRef() @@ -17,59 +20,22 @@ class Canvas extends React.Component { } this.modes = { + DEFAULT: '', PAN: 'pan', } - this.currentMode = 'pan' + this.currentMode = this.modes.DEFAULT this.updateCanvasDimensions = this.updateCanvasDimensions.bind(this) } componentDidMount() { - this.fabricCanvas = new fabric.Canvas(this.canvasRef.current, { - selection: false, - }) - const rect = new fabric.Rect({ - left: 100, - top: 100, - fill: 'red', - width: 200, - height: 100, - angle: 0 - }) - this.fabricCanvas.add(rect) - console.log("Mounted: ", rect) - fabric.FabricImage.fromURL("https://images.unsplash.com/photo-1722898614949-c9a315e35209?q=80&w=1974&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", - (oImg) => { - console.log("added image", oImg) - - const canvasWidth = this.fabricCanvas.getWidth() - const canvasHeight = this.fabricCanvas.getHeight() - - // Get the image's original dimensions - const imgWidth = oImg.getScaledWidth() - const imgHeight = oImg.getScaledHeight() - - // Set the position to the center - oImg.set({ - left: (canvasWidth - imgWidth) / 2, - top: (canvasHeight - imgHeight) / 2 - }) - - oImg.scaleX(2) - oImg.scaleY(2) - - // this.fabricCanvas.backgroundImage = oImg - this.fabricCanvas.add(oImg) - this.fabricCanvas.renderAll() - }) - - this.initPan() - + this.initCanvas() window.addEventListener("resize", this.updateCanvasDimensions) this.updateCanvasDimensions() } componentWillUnmount() { + this.fabricCanvas.clear() this.fabricCanvas.dispose() window.removeEventListener("resize", this.updateCanvasDimensions) } @@ -88,13 +54,44 @@ class Canvas extends React.Component { this.fabricCanvas.renderAll() } - initPan(){ + initCanvas(){ + this.fabricCanvas = new fabric.Canvas(this.canvasRef.current, { + selection: false, + }) + + this.fabricCanvas.hoverCursor = this.fabricCanvas.moveCursor = 'pointer' + + const rect = new fabric.Rect({ + left: 100, + top: 100, + fill: 'red', + width: 200, + height: 100, + angle: 0 + }) + this.fabricCanvas.add(rect) + fabric.FabricImage.fromURL("https://images.unsplash.com/photo-1722898614949-c9a315e35209?q=80&w=1974&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D").then((img) => { + + img.scale(0.2) + this.fabricCanvas.add(img) + }) + + // this.fabricCanvas.renderAll() + this.initEvents() + + } + + initEvents(){ this.fabricCanvas.on("mouse:down", (event) => { this.mousePressed = true - this.startX = event.e.clientX - this.startY = event.e.clientY + if (this.fabricCanvas.getActiveObjects().length === 0){ + this.currentMode = this.modes.PAN + } + + this.mousePos.startX = event.e.clientX + this.mousePos.startY = event.e.clientY this.fabricCanvas.setCursor("grab") @@ -108,10 +105,12 @@ class Canvas extends React.Component { this.fabricCanvas.on("mouse:move", (event) => { - if (this.mousePressed && this.currentMode === this.modes.PAN){ + if (event.e.buttons === 1 && this.currentMode === this.modes.PAN){ this.fabricCanvas.setCursor("grab") + // TODO: add panning limits + const deltaX = event.e.clientX - this.mousePos.startX const deltaY = event.e.clientY - this.mousePos.startY @@ -124,11 +123,84 @@ class Canvas extends React.Component { }) + this.fabricCanvas.on("selection:created", () => { + console.log("selected") + this.currentMode = this.modes.DEFAULT + }) + + this.fabricCanvas.on('mouse:wheel', (opt) => { + // console.log("opt: ", opt) + let delta = opt.e.deltaY + let zoom = this.fabricCanvas.getZoom() + zoom *= 0.999 ** delta + + if (zoom > 20) zoom = 20 + if (zoom < 0.01) zoom = 0.01 + + this.fabricCanvas.zoomToPoint({ x: opt.e.offsetX, y: opt.e.offsetY }, zoom) + + opt.e.preventDefault() + opt.e.stopPropagation() + }) + + + } + + getCanvasObjectsBoundingBox(padding = 0) { + const objects = this.fabricCanvas.getObjects() + if (objects.length === 0) { + return { left: 0, top: 0, width: this.fabricCanvas.width, height: this.fabricCanvas.height } + } + + const boundingBox = objects.reduce((acc, obj) => { + const objBoundingBox = obj.getBoundingRect(true) + acc.left = Math.min(acc.left, objBoundingBox.left) + acc.top = Math.min(acc.top, objBoundingBox.top) + acc.right = Math.max(acc.right, objBoundingBox.left + objBoundingBox.width) + acc.bottom = Math.max(acc.bottom, objBoundingBox.top + objBoundingBox.height) + return acc + }, { + left: Infinity, + top: Infinity, + right: -Infinity, + bottom: -Infinity + }) + + // Adding padding + boundingBox.left -= padding + boundingBox.top -= padding + boundingBox.right += padding + boundingBox.bottom += padding + + return { + left: boundingBox.left, + top: boundingBox.top, + width: boundingBox.right - boundingBox.left, + height: boundingBox.bottom - boundingBox.top + } + } + + + resetZoom(){ + this.fabricCanvas.setViewportTransform([1,0,0,1,0,0]) + } + + resetPan(){ + this.fabricCanvas.setViewportTransform([1,0,0,1,0,0]) } render() { return (
+ +
+ + +
+ {/* Don't add any bg color here */}
)