-
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 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.
Reference in New Issue
Block a user