在我的应用程序中,我想在全屏和页内模式之间保持画布的状态。现在,我正在尝试通过在画布上下文中以 JSON 格式保存画布对象来实现此目的。切换模式时,会创建一个新的画布实例,然后我将 JSON 中的对象加载到该新的画布实例中。
问题是对象没有显示出来。当我在执行此 JSON 加载后记录画布实例的对象时,我可以看到它们存在于画布中。然而,当我查看页面时,它们在画布上根本不可见。
我加载的 JSON 错误吗?新画布的大小可能与旧画布不同,但这并不是对象被拉伸或扭曲,它们只是不存在。即使当我切换回旧的画布尺寸时,也看不到任何东西。
文档没有提到这一点,而且我没有发现其他人有这个问题。
以下是我的代码,仅限于与问题相关的内容。
我在加载 JSON 元素后立即渲染画布。
import { useCanvas } from "@/contexts/CanvasContext";
import React, { useEffect, useRef, useState } from "react";
declare global {
interface Window {
fabric: any;
}
}
type FabricCanvasProps = {
dimensions: { width: number; height: number };
canvasInstance: any;
setCanvasInstance: (canvasInstance: any) => void;
};
const FabricCanvas: React.FC<FabricCanvasProps> = ({
dimensions,
canvasInstance,
setCanvasInstance,
}) => {
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const [prevDimensions, setPrevDimensions] = useState({ width: 0, height: 0 });
const {
activeTool,
setIsDrawnOn,
canvasData,
setCanvasData,
} = useCanvas();
useEffect(() => {
const fabricScriptLoaded = () => !!window.fabric;
if (canvasRef.current && !canvasInstance && fabricScriptLoaded()) {
const instance = new window.fabric.Canvas(canvasRef.current, {
isDrawingMode: activeTool !== null,
height: dimensions.height,
width: dimensions.width,
selection: false,
});
if (canvasData) {
instance.loadFromJSON(canvasData, instance.renderAll.bind(instance));
console.log(canvasData);
console.log(instance.getObjects());
}
// Listen for changes to the canvas
const updateDrawnState = () => {
setIsDrawnOn(true);
};
instance.on("object:added", updateDrawnState);
instance.on("object:modified", updateDrawnState);
setCanvasInstance(instance);
}
}, []); // Initializes the canvas instance only once
// Updates the context with most recent canvas data for re-rendering
useEffect(() => {
if (canvasInstance) {
// Listen for any change that should update the canvas data
const onChange = () => {
setCanvasData(canvasInstance.toJSON());
};
canvasInstance.on("object:added", onChange);
canvasInstance.on("object:modified", onChange);
canvasInstance.on("object:removed", onChange);
return () => {
canvasInstance.off("object:added", onChange);
canvasInstance.off("object:modified", onChange);
canvasInstance.off("object:removed", onChange);
};
}
}, [canvasInstance]);
// ... Some more useEffects that have to do with canvas size
return (
<>
<canvas ref={canvasRef} className="w-full h-full" />
</>
);
};
export default FabricCanvas;
我最终发现了这一点:它实际上确实与我认为无关的画布更新有关。我使用以下代码随着页面大小的变化调整画布内对象的大小:
const scaleX = dimensions.width / prevDimensions.width;
const scaleY = dimensions.height / prevDimensions.height;
canvasInstance.getObjects().forEach((obj: any) => {
obj.scaleX *= scaleX;
obj.scaleY *= scaleY;
obj.left *= scaleX;
obj.top *= scaleY;
obj.setCoords();
}
但是当组件最初渲染时,我的 prevDimensions 为 0。这导致对象缩放到无限大小,同时不会抛出任何错误或指示出现问题,因此我看不到它们。
我通过添加检查来修复此问题,以确保此处不会发生除以 0 的情况。