-
This commit is contained in:
@@ -103,6 +103,7 @@ const EditorCanvas = forwardRef<EditorCanvasRef, EditorCanvasProps>(
|
||||
const isTransformingRef = useRef(false); // True while user is actively dragging/scaling
|
||||
const guideLinesRef = useRef<fabric.Line[]>([]);
|
||||
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
|
||||
|
||||
// Keep templateElementsRef in sync
|
||||
@@ -1005,6 +1006,15 @@ const EditorCanvas = forwardRef<EditorCanvasRef, EditorCanvasProps>(
|
||||
|
||||
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
|
||||
template.elements.forEach((element) => {
|
||||
if (element.type !== "image" || !element.imageSettings?.src) return;
|
||||
@@ -1053,24 +1063,55 @@ const EditorCanvas = forwardRef<EditorCanvasRef, EditorCanvasProps>(
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip if this image is already being loaded
|
||||
if (loadingImagesRef.current.has(element.id)) {
|
||||
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
|
||||
const imgElement = new Image();
|
||||
imgElement.crossOrigin = "anonymous";
|
||||
imgElement.onload = () => {
|
||||
// Remove from loading set
|
||||
loadingImagesRef.current.delete(elementId);
|
||||
|
||||
if (!fabricRef.current) return;
|
||||
|
||||
const pos = element.position;
|
||||
const imgSettings = element.imageSettings!;
|
||||
// Re-fetch current element data from template (might have changed during load)
|
||||
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 targetHeight = mmToPx(pos.height) * zoom;
|
||||
const imgNaturalWidth = imgElement.naturalWidth;
|
||||
const imgNaturalHeight = imgElement.naturalHeight;
|
||||
|
||||
// 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 scaleY = targetHeight / imgNaturalHeight;
|
||||
|
||||
@@ -1083,33 +1124,35 @@ const EditorCanvas = forwardRef<EditorCanvasRef, EditorCanvasProps>(
|
||||
opacity: imgSettings.opacity ?? 1,
|
||||
flipX: imgSettings.flipHorizontal || false,
|
||||
flipY: imgSettings.flipVertical || false,
|
||||
hasControls: !element.locked,
|
||||
hasBorders: !element.locked,
|
||||
lockMovementX: element.locked,
|
||||
lockMovementY: element.locked,
|
||||
lockRotation: element.locked,
|
||||
lockScalingX: element.locked,
|
||||
lockScalingY: element.locked,
|
||||
hasControls: !currentElement.locked,
|
||||
hasBorders: !currentElement.locked,
|
||||
lockMovementX: currentElement.locked,
|
||||
lockMovementY: currentElement.locked,
|
||||
lockRotation: currentElement.locked,
|
||||
lockScalingX: currentElement.locked,
|
||||
lockScalingY: currentElement.locked,
|
||||
});
|
||||
|
||||
(fabricImage as FabricObjectWithData).data = {
|
||||
id: element.id,
|
||||
type: element.type,
|
||||
id: elementId,
|
||||
type: elementType,
|
||||
};
|
||||
|
||||
// Remove placeholder and add real image
|
||||
canvas.remove(existingObj);
|
||||
// Remove the current placeholder/old image and add the new one
|
||||
canvas.remove(currentObj);
|
||||
canvas.add(fabricImage);
|
||||
elementsMapRef.current.set(
|
||||
element.id,
|
||||
elementId,
|
||||
fabricImage as FabricObjectWithData,
|
||||
);
|
||||
loadedImagesRef.current.set(element.id, currentSrc);
|
||||
loadedImagesRef.current.set(elementId, currentSrc);
|
||||
|
||||
canvas.renderAll();
|
||||
};
|
||||
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;
|
||||
});
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user