В моем приложении я хотел бы поддерживать состояние холста между полноэкранным и внутристраничным режимами. Прямо сейчас я пытаюсь добиться этого, сохраняя объекты холста в формате 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.info(canvasData);
console.info(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;
🤔 А знаете ли вы, что...
React обеспечивает реактивное обновление интерфейса при изменении состояния.
В конце концов я понял это: на самом деле это было связано с обновлениями холста, которые, как я думал, не были связаны. Я использовал следующий код для изменения размера объектов на холсте при изменении размера страницы:
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.