From 65770880ab325e1478fdc079a1b34bc431a28e0d Mon Sep 17 00:00:00 2001 From: zihanjian Date: Sun, 25 May 2025 01:11:25 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=B8=80=E9=94=AE=E6=93=A6?= =?UTF-8?q?=E9=99=A4=E6=A8=A1=E5=BC=8F=EF=BC=8C=E5=85=81=E8=AE=B8=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E9=80=9A=E8=BF=87=E6=B4=AA=E6=B0=B4=E5=A1=AB=E5=85=85?= =?UTF-8?q?=E6=96=B9=E5=BC=8F=E5=88=A0=E9=99=A4=E7=9B=B8=E5=90=8C=E9=A2=9C?= =?UTF-8?q?=E8=89=B2=E7=9A=84=E5=83=8F=E7=B4=A0=E3=80=82=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E7=8A=B6=E6=80=81=E7=AE=A1=E7=90=86=E5=92=8C?= =?UTF-8?q?=E4=BA=A4=E4=BA=92=E9=80=BB=E8=BE=91=EF=BC=8C=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E8=B0=83=E8=89=B2=E6=9D=BF=E7=BB=84=E4=BB=B6=E4=BB=A5=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E6=93=A6=E9=99=A4=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=8F=90?= =?UTF-8?q?=E5=8D=87=E7=94=A8=E6=88=B7=E4=BD=93=E9=AA=8C=E5=92=8C=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E5=8F=AF=E8=AF=BB=E6=80=A7=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/page.tsx | 102 +++++++++++++++++++++++++++++++- src/components/ColorPalette.tsx | 32 +++++++++- 2 files changed, 132 insertions(+), 2 deletions(-) diff --git a/src/app/page.tsx b/src/app/page.tsx index d4f5c51..98ad635 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -156,6 +156,8 @@ export default function Home() { const [remapTrigger, setRemapTrigger] = useState(0); const [isManualColoringMode, setIsManualColoringMode] = useState(false); const [selectedColor, setSelectedColor] = useState(null); + // 新增:一键擦除模式状态 + const [isEraseMode, setIsEraseMode] = useState(false); // 新增状态变量:控制打赏弹窗 const [isDonationModalOpen, setIsDonationModalOpen] = useState(false); const [customPaletteSelections, setCustomPaletteSelections] = useState({}); @@ -309,6 +311,21 @@ export default function Home() { // ++ Reset manual coloring mode when a new file is processed ++ setIsManualColoringMode(false); setSelectedColor(null); + setIsEraseMode(false); + }; + + // 处理一键擦除模式切换 + const handleEraseToggle = () => { + // 确保在手动上色模式下才能使用擦除功能 + if (!isManualColoringMode) { + return; + } + + setIsEraseMode(!isEraseMode); + // 如果开启擦除模式,取消选中的颜色 + if (!isEraseMode) { + setSelectedColor(null); + } }; // ++ 新增:处理输入框变化的函数 ++ @@ -824,6 +841,74 @@ export default function Home() { // --- Canvas Interaction --- + // 洪水填充擦除函数 + const floodFillErase = (startRow: number, startCol: number, targetKey: string) => { + if (!mappedPixelData || !gridDimensions) return; + + const { N, M } = gridDimensions; + const newPixelData = mappedPixelData.map(row => row.map(cell => ({ ...cell }))); + const visited = Array(M).fill(null).map(() => Array(N).fill(false)); + + // 使用栈实现非递归洪水填充 + const stack = [{ row: startRow, col: startCol }]; + + while (stack.length > 0) { + const { row, col } = stack.pop()!; + + // 检查边界 + if (row < 0 || row >= M || col < 0 || col >= N || visited[row][col]) { + continue; + } + + const currentCell = newPixelData[row][col]; + + // 检查是否是目标颜色且不是外部区域 + if (!currentCell || currentCell.isExternal || currentCell.key !== targetKey) { + continue; + } + + // 标记为已访问 + visited[row][col] = true; + + // 擦除当前像素(设为透明) + newPixelData[row][col] = { ...transparentColorData }; + + // 添加相邻像素到栈中 + stack.push( + { row: row - 1, col }, // 上 + { row: row + 1, col }, // 下 + { row, col: col - 1 }, // 左 + { row, col: col + 1 } // 右 + ); + } + + // 更新状态 + setMappedPixelData(newPixelData); + + // 重新计算颜色统计 + if (colorCounts) { + const newColorCounts: { [key: string]: { count: number; color: string } } = {}; + let newTotalCount = 0; + + newPixelData.flat().forEach(cell => { + if (cell && !cell.isExternal && cell.key !== TRANSPARENT_KEY) { + if (!newColorCounts[cell.key]) { + const colorInfo = fullBeadPalette.find(p => p.key === cell.key); + newColorCounts[cell.key] = { + count: 0, + color: colorInfo?.hex || '#000000' + }; + } + newColorCounts[cell.key].count++; + newTotalCount++; + } + }); + + setColorCounts(newColorCounts); + setTotalBeadCount(newTotalCount); + } + }; + // ++ Re-introduce the combined interaction handler ++ const handleCanvasInteraction = ( clientX: number, @@ -861,6 +946,17 @@ export default function Home() { if (i >= 0 && i < N && j >= 0 && j < M) { const cellData = mappedPixelData[j][i]; + // 一键擦除模式逻辑 + if (isClick && isEraseMode) { + if (cellData && !cellData.isExternal && cellData.key && cellData.key !== TRANSPARENT_KEY) { + // 执行洪水填充擦除 + floodFillErase(j, i, cellData.key); + setIsEraseMode(false); // 擦除完成后退出擦除模式 + setTooltipData(null); + } + return; + } + // Manual Coloring Logic - 保持原有的上色逻辑 if (isClick && isManualColoringMode && selectedColor) { // 手动上色模式逻辑保持不变 @@ -1022,6 +1118,7 @@ export default function Home() { // 退出手动上色模式 setIsManualColoringMode(false); setSelectedColor(null); + setIsEraseMode(false); }; // ++ 新增:导出自定义色板配置 ++ @@ -1419,6 +1516,7 @@ export default function Home() { setIsManualColoringMode(false); // Always exit mode here setSelectedColor(null); setTooltipData(null); + setIsEraseMode(false); // 重置擦除模式状态 }} className={`w-full py-2.5 px-4 text-sm sm:text-base rounded-lg transition-all duration-200 flex items-center justify-center gap-2 bg-red-500 hover:bg-red-600 text-white shadow-sm hover:shadow-md`} // Keep red for contrast? > @@ -1436,7 +1534,7 @@ export default function Home() { {/* Text color implicitly handled by parent */} - 选择颜色/橡皮擦,点击画布格子上色 + 选择颜色/橡皮擦/一键擦除,点击画布格子上色 {/* Separator color */} | @@ -1467,6 +1565,8 @@ export default function Home() { onColorSelect={setSelectedColor} transparentKey={TRANSPARENT_KEY} selectedColorSystem={selectedColorSystem} + isEraseMode={isEraseMode} + onEraseToggle={handleEraseToggle} /> diff --git a/src/components/ColorPalette.tsx b/src/components/ColorPalette.tsx index 75f39a5..3ee209c 100644 --- a/src/components/ColorPalette.tsx +++ b/src/components/ColorPalette.tsx @@ -16,6 +16,9 @@ interface ColorPaletteProps { onColorSelect: (colorData: ColorData) => void; transparentKey?: string; // 添加可选参数,用于识别哪个是透明/橡皮擦 selectedColorSystem?: ColorSystem; // 添加色号系统参数 + // 新增:一键擦除相关props + isEraseMode?: boolean; + onEraseToggle?: () => void; } const ColorPalette: React.FC = ({ @@ -23,7 +26,9 @@ const ColorPalette: React.FC = ({ selectedColor, onColorSelect, transparentKey, - selectedColorSystem + selectedColorSystem, + isEraseMode, + onEraseToggle }) => { if (!colors || colors.length === 0) { // Apply dark mode text color @@ -33,6 +38,31 @@ const ColorPalette: React.FC = ({ return ( // Apply dark mode styles to the container
+ {/* 一键擦除按钮 */} + {onEraseToggle && ( + + )} + {colors.map((colorData) => { // 检查当前颜色是否是透明/橡皮擦 const isTransparent = transparentKey && colorData.key === transparentKey;