This commit is contained in:
2025-11-29 00:59:22 +01:00
parent 30b4bb4626
commit d1320c520f
3 changed files with 61 additions and 18 deletions

View File

@@ -103,6 +103,7 @@ const EditorCanvas = forwardRef<EditorCanvasRef, EditorCanvasProps>(
const isTransformingRef = useRef(false); // True while user is actively dragging/scaling const isTransformingRef = useRef(false); // True while user is actively dragging/scaling
const guideLinesRef = useRef<fabric.Line[]>([]); const guideLinesRef = useRef<fabric.Line[]>([]);
const loadedImagesRef = useRef<Map<string, string>>(new Map()); // elementId -> loaded src const loadedImagesRef = useRef<Map<string, string>>(new Map()); // elementId -> loaded src
const loadingImagesRef = useRef<Set<string>>(new Set()); // elementIds currently being loaded
const templateElementsRef = useRef(template.elements); // Ref for accessing current elements in callbacks const templateElementsRef = useRef(template.elements); // Ref for accessing current elements in callbacks
// Keep templateElementsRef in sync // Keep templateElementsRef in sync
@@ -1005,6 +1006,15 @@ const EditorCanvas = forwardRef<EditorCanvasRef, EditorCanvasProps>(
const canvas = fabricRef.current; const canvas = fabricRef.current;
// Clean up loadedImagesRef for elements that no longer exist
const currentElementIds = new Set(template.elements.map((e) => e.id));
for (const elementId of loadedImagesRef.current.keys()) {
if (!currentElementIds.has(elementId)) {
loadedImagesRef.current.delete(elementId);
loadingImagesRef.current.delete(elementId);
}
}
// Find image elements that need loading // Find image elements that need loading
template.elements.forEach((element) => { template.elements.forEach((element) => {
if (element.type !== "image" || !element.imageSettings?.src) return; if (element.type !== "image" || !element.imageSettings?.src) return;
@@ -1053,24 +1063,55 @@ const EditorCanvas = forwardRef<EditorCanvasRef, EditorCanvasProps>(
return; return;
} }
// Skip if this image is already being loaded
if (loadingImagesRef.current.has(element.id)) {
return;
}
if (!existingObj) return; if (!existingObj) return;
// Mark as loading to prevent duplicate loads
loadingImagesRef.current.add(element.id);
// Capture element data for async callback
const elementId = element.id;
const elementType = element.type;
// Load the image // Load the image
const imgElement = new Image(); const imgElement = new Image();
imgElement.crossOrigin = "anonymous"; imgElement.crossOrigin = "anonymous";
imgElement.onload = () => { imgElement.onload = () => {
// Remove from loading set
loadingImagesRef.current.delete(elementId);
if (!fabricRef.current) return; if (!fabricRef.current) return;
const pos = element.position; // Re-fetch current element data from template (might have changed during load)
const imgSettings = element.imageSettings!; const currentElement = templateElementsRef.current.find(
(el) => el.id === elementId,
);
if (!currentElement || currentElement.type !== "image") return;
// Re-fetch the current object from the map (might have been recreated)
const currentObj = elementsMapRef.current.get(elementId);
if (!currentObj) return;
// Skip if already a FabricImage with correct src (another load completed first)
if (
currentObj instanceof fabric.FabricImage &&
loadedImagesRef.current.get(elementId) === currentSrc
) {
return;
}
const pos = currentElement.position;
const imgSettings = currentElement.imageSettings!;
const targetWidth = mmToPx(pos.width) * zoom; const targetWidth = mmToPx(pos.width) * zoom;
const targetHeight = mmToPx(pos.height) * zoom; const targetHeight = mmToPx(pos.height) * zoom;
const imgNaturalWidth = imgElement.naturalWidth; const imgNaturalWidth = imgElement.naturalWidth;
const imgNaturalHeight = imgElement.naturalHeight; const imgNaturalHeight = imgElement.naturalHeight;
// Simple approach: scale image to fit the target dimensions // Simple approach: scale image to fit the target dimensions
// The fitMode affects how we calculate initial dimensions when first adding,
// but once placed, the user's resize takes precedence
const scaleX = targetWidth / imgNaturalWidth; const scaleX = targetWidth / imgNaturalWidth;
const scaleY = targetHeight / imgNaturalHeight; const scaleY = targetHeight / imgNaturalHeight;
@@ -1083,33 +1124,35 @@ const EditorCanvas = forwardRef<EditorCanvasRef, EditorCanvasProps>(
opacity: imgSettings.opacity ?? 1, opacity: imgSettings.opacity ?? 1,
flipX: imgSettings.flipHorizontal || false, flipX: imgSettings.flipHorizontal || false,
flipY: imgSettings.flipVertical || false, flipY: imgSettings.flipVertical || false,
hasControls: !element.locked, hasControls: !currentElement.locked,
hasBorders: !element.locked, hasBorders: !currentElement.locked,
lockMovementX: element.locked, lockMovementX: currentElement.locked,
lockMovementY: element.locked, lockMovementY: currentElement.locked,
lockRotation: element.locked, lockRotation: currentElement.locked,
lockScalingX: element.locked, lockScalingX: currentElement.locked,
lockScalingY: element.locked, lockScalingY: currentElement.locked,
}); });
(fabricImage as FabricObjectWithData).data = { (fabricImage as FabricObjectWithData).data = {
id: element.id, id: elementId,
type: element.type, type: elementType,
}; };
// Remove placeholder and add real image // Remove the current placeholder/old image and add the new one
canvas.remove(existingObj); canvas.remove(currentObj);
canvas.add(fabricImage); canvas.add(fabricImage);
elementsMapRef.current.set( elementsMapRef.current.set(
element.id, elementId,
fabricImage as FabricObjectWithData, fabricImage as FabricObjectWithData,
); );
loadedImagesRef.current.set(element.id, currentSrc); loadedImagesRef.current.set(elementId, currentSrc);
canvas.renderAll(); canvas.renderAll();
}; };
imgElement.onerror = () => { imgElement.onerror = () => {
console.error(`Failed to load image for element ${element.id}`); // Remove from loading set on error too
loadingImagesRef.current.delete(elementId);
console.error(`Failed to load image for element ${elementId}`);
}; };
imgElement.src = currentSrc; imgElement.src = currentSrc;
}); });

Binary file not shown.

Binary file not shown.