新增高亮颜色功能,更新相关组件以支持颜色高亮效果,优化像素化画布绘制逻辑,提升用户体验和代码可读性。

This commit is contained in:
zihanjian
2025-05-25 12:14:31 +08:00
parent ceff488dbf
commit f8ea1a94af
4 changed files with 69 additions and 17 deletions

View File

@@ -141,6 +141,9 @@ export default function Home() {
includeStats: true // 默认包含统计信息
});
// 新增:高亮相关状态
const [highlightColorKey, setHighlightColorKey] = useState<string | null>(null);
const originalCanvasRef = useRef<HTMLCanvasElement>(null);
const pixelatedCanvasRef = useRef<HTMLCanvasElement>(null);
const fileInputRef = useRef<HTMLInputElement>(null);
@@ -1182,6 +1185,15 @@ export default function Home() {
importPaletteInputRef.current?.click();
};
// 新增:处理颜色高亮
const handleHighlightColor = (colorHex: string) => {
setHighlightColorKey(colorHex);
};
// 新增:高亮完成回调
const handleHighlightComplete = () => {
setHighlightColorKey(null);
};
return (
<>
@@ -1540,6 +1552,7 @@ export default function Home() {
selectedColorSystem={selectedColorSystem}
isEraseMode={isEraseMode}
onEraseToggle={handleEraseToggle}
onHighlightColor={handleHighlightColor}
/>
</div>
</div>
@@ -1558,6 +1571,8 @@ export default function Home() {
gridDimensions={gridDimensions}
isManualColoringMode={isManualColoringMode}
onInteraction={handleCanvasInteraction}
highlightColorKey={highlightColorKey}
onHighlightComplete={handleHighlightComplete}
/>
</div>
</div>

View File

@@ -19,6 +19,8 @@ interface ColorPaletteProps {
// 新增一键擦除相关props
isEraseMode?: boolean;
onEraseToggle?: () => void;
// 新增高亮相关props
onHighlightColor?: (colorHex: string) => void; // 触发高亮某个颜色
}
const ColorPalette: React.FC<ColorPaletteProps> = ({
@@ -28,7 +30,8 @@ const ColorPalette: React.FC<ColorPaletteProps> = ({
transparentKey,
selectedColorSystem,
isEraseMode,
onEraseToggle
onEraseToggle,
onHighlightColor
}) => {
if (!colors || colors.length === 0) {
// Apply dark mode text color
@@ -71,7 +74,13 @@ const ColorPalette: React.FC<ColorPaletteProps> = ({
return (
<button
key={colorData.key}
onClick={() => onColorSelect(colorData)}
onClick={() => {
onColorSelect(colorData);
// 如果不是透明颜色且有高亮回调,触发高亮效果
if (!isTransparent && onHighlightColor) {
onHighlightColor(colorData.color);
}
}}
className={`w-8 h-8 rounded border-2 flex-shrink-0 transition-transform transform hover:scale-110 focus:outline-none focus:ring-2 focus:ring-offset-1 focus:ring-blue-400 dark:focus:ring-blue-500 ${
isSelected
// Apply dark mode styles for selected state

View File

@@ -16,28 +16,30 @@ interface PixelatedPreviewCanvasProps {
isClick: boolean,
isTouchEnd?: boolean
) => void;
highlightColorKey?: string | null;
onHighlightComplete?: () => void;
}
// 绘制像素化画布的函数
const drawPixelatedCanvas = (
dataToDraw: MappedPixel[][],
canvas: HTMLCanvasElement | null,
dims: { N: number; M: number } | null
dims: { N: number; M: number } | null,
highlightColorKey?: string | null,
isHighlighting?: boolean
) => {
if (!canvas || !dims || dims.N <= 0 || dims.M <= 0) {
console.warn("无法绘制Canvas参数无效或数据未准备好");
const ctx = canvas?.getContext('2d');
if (ctx && canvas) ctx.clearRect(0, 0, canvas.width, canvas.height);
return;
}
const pixelatedCtx = canvas.getContext('2d');
if (!pixelatedCtx) {
console.error("无法获取Canvas绘图上下文");
if (!canvas || !dims || !dataToDraw) {
console.warn("drawPixelatedCanvas: Missing required parameters");
return;
}
// Check if dark mode is active on the HTML element
const pixelatedCtx = canvas.getContext('2d');
if (!pixelatedCtx) {
console.error("Failed to get 2D context for pixelated canvas");
return;
}
// Respect current dark mode preference
const isDarkMode = typeof window !== 'undefined' && document.documentElement.classList.contains('dark');
// Define colors based on mode
@@ -69,6 +71,15 @@ const drawPixelatedCanvas = (
}
pixelatedCtx.fillRect(drawX, drawY, cellWidthOutput, cellHeightOutput);
// 如果正在高亮且当前单元格不是目标颜色,添加半透明黑色蒙版
if (isHighlighting && highlightColorKey && !cellData.isExternal) {
const shouldDim = cellData.color.toUpperCase() !== highlightColorKey.toUpperCase();
if (shouldDim) {
pixelatedCtx.fillStyle = 'rgba(0, 0, 0, 0.6)'; // 60% 透明度的黑色蒙版
pixelatedCtx.fillRect(drawX, drawY, cellWidthOutput, cellHeightOutput);
}
}
// Draw grid lines using mode-specific color
pixelatedCtx.strokeStyle = gridLineColor;
pixelatedCtx.strokeRect(drawX + 0.5, drawY + 0.5, cellWidthOutput, cellHeightOutput);
@@ -82,10 +93,13 @@ const PixelatedPreviewCanvas: React.FC<PixelatedPreviewCanvasProps> = ({
isManualColoringMode,
canvasRef,
onInteraction,
highlightColorKey,
onHighlightComplete,
}) => {
const [darkModeState, setDarkModeState] = useState<boolean | null>(null);
const touchStartPosRef = useRef<{ x: number; y: number; pageX: number; pageY: number } | null>(null);
const touchMovedRef = useRef<boolean>(false);
const [isHighlighting, setIsHighlighting] = useState(false);
// Effect to detect dark mode changes and update state
useEffect(() => {
@@ -116,9 +130,23 @@ const PixelatedPreviewCanvas: React.FC<PixelatedPreviewCanvasProps> = ({
// Ensure darkModeState is not null before drawing
if (mappedPixelData && gridDimensions && canvasRef.current && darkModeState !== null) {
console.log(`Redrawing canvas, dark mode: ${darkModeState}`); // Log redraw trigger
drawPixelatedCanvas(mappedPixelData, canvasRef.current, gridDimensions);
drawPixelatedCanvas(mappedPixelData, canvasRef.current, gridDimensions, highlightColorKey, isHighlighting);
}
}, [mappedPixelData, gridDimensions, canvasRef, darkModeState]); // Add darkModeState dependency
}, [mappedPixelData, gridDimensions, canvasRef, darkModeState, highlightColorKey, isHighlighting]); // Add darkModeState dependency
// 处理高亮效果
useEffect(() => {
if (highlightColorKey && mappedPixelData && gridDimensions) {
setIsHighlighting(true);
// 0.3秒后结束高亮
const timer = setTimeout(() => {
setIsHighlighting(false);
onHighlightComplete?.();
}, 300);
return () => clearTimeout(timer);
}
}, [highlightColorKey, mappedPixelData, gridDimensions, onHighlightComplete]);
// --- 鼠标事件处理 ---

View File

@@ -226,7 +226,7 @@ export async function downloadImage({
ctx.textBaseline = 'middle';
// 添加文字阴影效果
ctx.shadowColor = 'rgba(0, 0, 0, 0.3)';
ctx.shadowColor = 'rgba(0, 0, 0, 0.5)';
ctx.shadowBlur = 5;
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;