fix: resync realtime canvas after mode switch
This commit is contained in:
parent
8757f76b1f
commit
36f7eea13b
@ -804,6 +804,7 @@ export function RealtimeCanvasDialog({
|
||||
const layerObjectUrlsRef = useRef<Set<string>>(new Set());
|
||||
const sidebarDraggingLayerIdRef = useRef<string | null>(null);
|
||||
const drawingHasContentRef = useRef(false);
|
||||
const drawingLayerSnapshotRef = useRef("");
|
||||
const dragDepthRef = useRef(0);
|
||||
|
||||
// History Management
|
||||
@ -876,9 +877,9 @@ export function RealtimeCanvasDialog({
|
||||
);
|
||||
const enhanceSourceImageUrl = useMemo(() => {
|
||||
return (
|
||||
previewUrl ||
|
||||
selectedImageLayer?.src ||
|
||||
imageLayers[0]?.src ||
|
||||
previewUrl ||
|
||||
initialImageUrl ||
|
||||
""
|
||||
);
|
||||
@ -1640,6 +1641,19 @@ export function RealtimeCanvasDialog({
|
||||
[adaptiveThrottleMs, markCanvasDirty, pumpFlush]
|
||||
);
|
||||
|
||||
const cancelPendingRealtimeFlush = useCallback((clearDirty = false) => {
|
||||
if (pendingFlushTimerRef.current) {
|
||||
window.clearTimeout(pendingFlushTimerRef.current);
|
||||
pendingFlushTimerRef.current = null;
|
||||
}
|
||||
pendingFlushModeRef.current = null;
|
||||
pendingFlushForceRef.current = false;
|
||||
dirtyRegionRef.current = null;
|
||||
if (clearDirty) {
|
||||
dirtyRef.current = false;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const reorderLayersFromSidebar = useCallback(
|
||||
(draggedLayerId: string, targetLayerId: string) => {
|
||||
if (!draggedLayerId || !targetLayerId || draggedLayerId === targetLayerId)
|
||||
@ -1796,16 +1810,83 @@ export function RealtimeCanvasDialog({
|
||||
|
||||
const canvas = canvasRef.current;
|
||||
const ctx = canvas?.getContext("2d");
|
||||
if (!canvas || !ctx) return;
|
||||
if (!canvas || !ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearDrawingCanvas();
|
||||
const image = await loadRenderableImage(dataUrl);
|
||||
const cssWidth = Math.max(
|
||||
1,
|
||||
Number.parseFloat(canvas.style.width) || canvas.width
|
||||
);
|
||||
const dpr = Math.max(1, canvas.width / cssWidth);
|
||||
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
|
||||
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
ctx.scale(dpr, dpr);
|
||||
drawingHasContentRef.current = true;
|
||||
},
|
||||
[clearDrawingCanvas, loadRenderableImage]
|
||||
);
|
||||
|
||||
const captureDrawingLayerSnapshot = useCallback(() => {
|
||||
if (!canvasRef.current) {
|
||||
return;
|
||||
}
|
||||
drawingLayerSnapshotRef.current = drawingCanvasHasContent(true)
|
||||
? exportDrawingLayerDataUrl()
|
||||
: "";
|
||||
}, [drawingCanvasHasContent, exportDrawingLayerDataUrl]);
|
||||
|
||||
const syncRealtimeCanvasAfterRemount = useCallback(async () => {
|
||||
const canvas = canvasRef.current;
|
||||
const imageLayerCanvas = imageLayerCanvasRef.current;
|
||||
const previewCanvas = previewCanvasRef.current;
|
||||
const wrap = canvasWrapRef.current;
|
||||
if (!canvas || !imageLayerCanvas || !previewCanvas || !wrap) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const rect = wrap.getBoundingClientRect();
|
||||
const cssWidth = Math.max(1, Math.floor(rect.width));
|
||||
const cssHeight = Math.max(1, Math.floor(rect.height));
|
||||
if (cssWidth <= 1 || cssHeight <= 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const dpr = Math.max(1, window.devicePixelRatio || 1);
|
||||
const pixelWidth = Math.max(1, Math.floor(cssWidth * dpr));
|
||||
const pixelHeight = Math.max(1, Math.floor(cssHeight * dpr));
|
||||
const configureCanvas = (targetCanvas: HTMLCanvasElement) => {
|
||||
targetCanvas.width = pixelWidth;
|
||||
targetCanvas.height = pixelHeight;
|
||||
targetCanvas.style.width = `${cssWidth}px`;
|
||||
targetCanvas.style.height = `${cssHeight}px`;
|
||||
const ctx = targetCanvas.getContext("2d");
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
ctx.scale(dpr, dpr);
|
||||
ctx.clearRect(0, 0, cssWidth, cssHeight);
|
||||
};
|
||||
|
||||
const drawingSnapshot = drawingLayerSnapshotRef.current;
|
||||
configureCanvas(imageLayerCanvas);
|
||||
configureCanvas(canvas);
|
||||
configureCanvas(previewCanvas);
|
||||
|
||||
if (drawingSnapshot) {
|
||||
await loadDrawingLayerFromDataUrl(drawingSnapshot);
|
||||
} else {
|
||||
drawingHasContentRef.current = false;
|
||||
}
|
||||
await renderImageLayers();
|
||||
return true;
|
||||
}, [loadDrawingLayerFromDataUrl, renderImageLayers]);
|
||||
|
||||
const openSavedProject = useCallback(
|
||||
async (projectId: string) => {
|
||||
const project = readRealtimeProjectsFromStorage().find(
|
||||
@ -1956,6 +2037,23 @@ export function RealtimeCanvasDialog({
|
||||
previewCtx.clearRect(0, 0, previewCanvas.width, previewCanvas.height);
|
||||
}, []);
|
||||
|
||||
const resetRealtimeInteractionState = useCallback(() => {
|
||||
pointerDownRef.current = false;
|
||||
draggingLayerIdRef.current = null;
|
||||
resizingLayerIdRef.current = null;
|
||||
layerDragOriginRef.current = null;
|
||||
layerResizeOriginRef.current = null;
|
||||
lastPointRef.current = null;
|
||||
shapeStartPointRef.current = null;
|
||||
sidebarDraggingLayerIdRef.current = null;
|
||||
dragDepthRef.current = 0;
|
||||
setIsDragImportActive(false);
|
||||
clearPreviewCanvas();
|
||||
if (cursorRef.current) {
|
||||
cursorRef.current.style.opacity = "0";
|
||||
}
|
||||
}, [clearPreviewCanvas]);
|
||||
|
||||
const undo = useCallback(() => {
|
||||
if (historyIndexRef.current <= 0) return;
|
||||
|
||||
@ -2557,6 +2655,36 @@ export function RealtimeCanvasDialog({
|
||||
};
|
||||
}, [drawInitialImage, initializeCanvas, open]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
return;
|
||||
}
|
||||
if (workspaceMode !== "realtime") {
|
||||
cancelPendingRealtimeFlush(true);
|
||||
resetRealtimeInteractionState();
|
||||
return;
|
||||
}
|
||||
|
||||
let frameId = 0;
|
||||
let followUpFrameId = 0;
|
||||
frameId = window.requestAnimationFrame(() => {
|
||||
followUpFrameId = window.requestAnimationFrame(() => {
|
||||
void syncRealtimeCanvasAfterRemount();
|
||||
});
|
||||
});
|
||||
|
||||
return () => {
|
||||
window.cancelAnimationFrame(frameId);
|
||||
window.cancelAnimationFrame(followUpFrameId);
|
||||
};
|
||||
}, [
|
||||
cancelPendingRealtimeFlush,
|
||||
open,
|
||||
resetRealtimeInteractionState,
|
||||
syncRealtimeCanvasAfterRemount,
|
||||
workspaceMode,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
|
||||
@ -2872,6 +3000,28 @@ export function RealtimeCanvasDialog({
|
||||
setEraserPopoverOpen(false);
|
||||
}, []);
|
||||
|
||||
const handleWorkspaceModeChange = useCallback(
|
||||
(nextMode: RealtimeWorkspaceMode) => {
|
||||
if (nextMode === workspaceMode) {
|
||||
return;
|
||||
}
|
||||
if (workspaceMode === "realtime") {
|
||||
captureDrawingLayerSnapshot();
|
||||
}
|
||||
closeBrushSettingPopovers();
|
||||
cancelPendingRealtimeFlush(true);
|
||||
resetRealtimeInteractionState();
|
||||
setWorkspaceMode(nextMode);
|
||||
},
|
||||
[
|
||||
cancelPendingRealtimeFlush,
|
||||
captureDrawingLayerSnapshot,
|
||||
closeBrushSettingPopovers,
|
||||
resetRealtimeInteractionState,
|
||||
workspaceMode,
|
||||
]
|
||||
);
|
||||
|
||||
const activateSelectMode = useCallback(() => {
|
||||
setSelectedLayerId(DRAW_LAYER_ID);
|
||||
setBrushMode("select");
|
||||
@ -3008,7 +3158,7 @@ export function RealtimeCanvasDialog({
|
||||
? softActionButtonClass
|
||||
: "border border-transparent bg-transparent text-[#666666] hover:bg-white hover:text-[#111111]"
|
||||
)}
|
||||
onClick={() => setWorkspaceMode("realtime")}
|
||||
onClick={() => handleWorkspaceModeChange("realtime")}
|
||||
type="button"
|
||||
>
|
||||
<Paintbrush className="h-4 w-4" />
|
||||
@ -3021,7 +3171,7 @@ export function RealtimeCanvasDialog({
|
||||
? softActionButtonClass
|
||||
: "border border-transparent bg-transparent text-[#666666] hover:bg-white hover:text-[#111111]"
|
||||
)}
|
||||
onClick={() => setWorkspaceMode("enhance-pro")}
|
||||
onClick={() => handleWorkspaceModeChange("enhance-pro")}
|
||||
type="button"
|
||||
>
|
||||
<Wand2 className="h-4 w-4" />
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user