From eba188116ac92e77fd0c4cb56b5dadfac048c8f4 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 1 Mar 2026 01:57:16 +0800 Subject: [PATCH] fix(canvas): restore compare drag and ctrl+a image multi-select --- app/canvas/[id]/page.tsx | 19 ++++- components/canvas/nodes/ImageNode.tsx | 110 ++++++++++---------------- 2 files changed, 59 insertions(+), 70 deletions(-) diff --git a/app/canvas/[id]/page.tsx b/app/canvas/[id]/page.tsx index 2cefd5c63..fdfe5bae3 100644 --- a/app/canvas/[id]/page.tsx +++ b/app/canvas/[id]/page.tsx @@ -112,6 +112,7 @@ export default function CanvasEditorPage({ params }: PageProps) { const clearCanvas = useCanvasStore((state) => state.clearCanvas); const getCanvasState = useCanvasStore((state) => state.getCanvasState); const addNode = useCanvasStore((state) => state.addNode); + const selectNodes = useCanvasStore((state) => state.selectNodes); const updateNode = useCanvasStore((state) => state.updateNode); const setGenerating = useCanvasStore((state) => state.setGenerating); @@ -487,6 +488,22 @@ export default function CanvasEditorPage({ params }: PageProps) { e.preventDefault(); handleSave(); } + // Ctrl/Cmd + A:当已选中图片时,选中画布内全部图片 + if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'a') { + const { nodes: currentNodes, selectedNodeIds: currentSelectedNodeIds } = useCanvasStore.getState(); + const selectedHasImage = currentSelectedNodeIds.some((selectedId) => + currentNodes.some((node) => node.id === selectedId && node.type === "image") + ); + if (selectedHasImage) { + const allImageNodeIds = currentNodes + .filter((node) => node.type === "image" && Boolean((node.data as any)?.src)) + .map((node) => node.id); + if (allImageNodeIds.length > 0) { + e.preventDefault(); + selectNodes(allImageNodeIds); + } + } + } // Ctrl/Cmd + Z 撤销 if ((e.ctrlKey || e.metaKey) && e.key === 'z' && !e.shiftKey) { e.preventDefault(); @@ -513,7 +530,7 @@ export default function CanvasEditorPage({ params }: PageProps) { window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); - }, [handleSave, undo, redo, setTool]); + }, [handleSave, nodes, redo, selectNodes, selectedNodeIds, setTool, undo]); if (isLoading) { return ( diff --git a/components/canvas/nodes/ImageNode.tsx b/components/canvas/nodes/ImageNode.tsx index 87642d981..30c9341bd 100644 --- a/components/canvas/nodes/ImageNode.tsx +++ b/components/canvas/nodes/ImageNode.tsx @@ -264,7 +264,6 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) { // 文件输入的 ref const fileInputRef = useRef(null); const containerRef = useRef(null); - const compareFrameRef = useRef(null); const updateNode = useCanvasStore((state) => state.updateNode); const deleteNode = useCanvasStore((state) => state.deleteNode); @@ -338,44 +337,6 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) { } }, [hasCompareSource, isThisNodePicking]); - const updateCompareSplitByClientX = useCallback((clientX: number) => { - const frame = compareFrameRef.current; - if (!frame) return; - - const rect = frame.getBoundingClientRect(); - if (rect.width <= 0) return; - - const rawPercent = ((clientX - rect.left) / rect.width) * 100; - const clampedPercent = Math.max(0, Math.min(100, rawPercent)); - setCompareSplitPercent(clampedPercent); - }, []); - - const startCompareDrag = useCallback((clientX: number) => { - setIsCompareDragging(true); - updateCompareSplitByClientX(clientX); - }, [updateCompareSplitByClientX]); - - useEffect(() => { - if (!isCompareDragging) return; - - const handlePointerMove = (event: PointerEvent) => { - updateCompareSplitByClientX(event.clientX); - }; - const handlePointerEnd = () => { - setIsCompareDragging(false); - }; - - window.addEventListener("pointermove", handlePointerMove); - window.addEventListener("pointerup", handlePointerEnd); - window.addEventListener("pointercancel", handlePointerEnd); - - return () => { - window.removeEventListener("pointermove", handlePointerMove); - window.removeEventListener("pointerup", handlePointerEnd); - window.removeEventListener("pointercancel", handlePointerEnd); - }; - }, [isCompareDragging, updateCompareSplitByClientX]); - useEffect(() => { if (!isEditingTitle) { setDraftTitle(getImageNodeTitle(data, node.id)); @@ -1561,35 +1522,12 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
{isCompareMode && hasCompareSource ? (
{ - if (event.button !== 0) return; - event.preventDefault(); - event.stopPropagation(); - event.currentTarget.setPointerCapture(event.pointerId); - startCompareDrag(event.clientX); - }} - onPointerMove={(event) => { - if (!isCompareDragging) return; - event.preventDefault(); - event.stopPropagation(); - updateCompareSplitByClientX(event.clientX); - }} - onPointerUp={(event) => { - if (!isCompareDragging) return; - event.preventDefault(); - event.stopPropagation(); - setIsCompareDragging(false); - }} - onPointerCancel={() => { - setIsCompareDragging(false); - }} > { - event.preventDefault(); - event.stopPropagation(); - event.currentTarget.setPointerCapture(event.pointerId); - startCompareDrag(event.clientX); - }} + tabIndex={-1} title="拖动中线查看对比" aria-label="拖动中线查看对比" > <> + { + event.stopPropagation(); + setIsCompareDragging(true); + }} + onPointerUp={(event) => { + event.stopPropagation(); + setIsCompareDragging(false); + }} + onPointerCancel={() => { + setIsCompareDragging(false); + }} + onClick={(event) => { + event.stopPropagation(); + }} + onInput={(event) => { + const value = Number((event.target as HTMLInputElement).value); + if (!Number.isNaN(value)) { + setCompareSplitPercent(Math.max(0, Math.min(100, value))); + } + }} + onChange={(event) => { + const value = Number(event.target.value); + if (!Number.isNaN(value)) { + setCompareSplitPercent(Math.max(0, Math.min(100, value))); + } + }} + />
原图
@@ -1695,6 +1664,9 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {