xpaintdev / hooks /useCanvasFileUtils.ts
suisuyy
Rename project back to xpaintai in package.json and metadata.json; add QuestionMarkIcon component; enhance AI features with ask functionality in useAiFeatures hook and App component
02ce812
import React, { useCallback } from 'react';
import { getBlankCanvasDataURL } from '../utils/canvasUtils';
import { CanvasHistoryHook } from './useCanvasHistory';
import { ConfirmModalHook } from './useConfirmModal';
import { ToastMessage } from './useToasts';
interface UseCanvasFileUtilsProps {
canvasState: {
currentDataURL: string | null;
canvasWidth: number;
canvasHeight: number;
};
historyActions: {
updateCanvasState: CanvasHistoryHook['updateCanvasState'];
};
uiActions: {
showToast: (message: string, type: ToastMessage['type']) => void;
setZoomLevel: (zoom: number) => void;
};
confirmModalActions: {
requestConfirmation: ConfirmModalHook['requestConfirmation'];
};
}
export interface CanvasFileUtilsHook {
handleLoadImageFile: (event: React.ChangeEvent<HTMLInputElement>) => void;
handleExportImage: () => void;
handleClearCanvas: () => void;
handleCanvasSizeChange: (newWidth: number, newHeight: number) => void;
}
export const useCanvasFileUtils = ({
canvasState,
historyActions,
uiActions,
confirmModalActions,
}: UseCanvasFileUtilsProps): CanvasFileUtilsHook => {
const { currentDataURL, canvasWidth, canvasHeight } = canvasState;
const { updateCanvasState } = historyActions;
const { showToast, setZoomLevel } = uiActions;
const { requestConfirmation } = confirmModalActions;
const handleLoadImageFile = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
const imageDataUrl = e.target?.result as string;
if (imageDataUrl) {
const img = new Image();
img.onload = () => {
let imageToDrawWidth = img.naturalWidth;
let imageToDrawHeight = img.naturalHeight;
if (img.naturalWidth > canvasWidth || img.naturalHeight > canvasHeight) {
const widthRatio = canvasWidth / img.naturalWidth;
const heightRatio = canvasHeight / img.naturalHeight;
const scaleFactor = Math.min(widthRatio, heightRatio);
imageToDrawWidth = Math.max(1, Math.floor(img.naturalWidth * scaleFactor));
imageToDrawHeight = Math.max(1, Math.floor(img.naturalHeight * scaleFactor));
}
const finalCanvasWidth = canvasWidth;
const finalCanvasHeight = canvasHeight;
const tempCanvas = document.createElement('canvas');
tempCanvas.width = finalCanvasWidth;
tempCanvas.height = finalCanvasHeight;
const tempCtx = tempCanvas.getContext('2d');
if (tempCtx) {
tempCtx.fillStyle = '#FFFFFF';
tempCtx.fillRect(0, 0, finalCanvasWidth, finalCanvasHeight);
// Change: Draw image at top-left (0,0)
const drawX = 0;
const drawY = 0;
tempCtx.drawImage(img, drawX, drawY, imageToDrawWidth, imageToDrawHeight);
const compositeDataURL = tempCanvas.toDataURL('image/png');
updateCanvasState(compositeDataURL, finalCanvasWidth, finalCanvasHeight);
showToast('Image loaded successfully.', 'success');
}
};
img.onerror = () => showToast("Error loading image file for processing.", 'error');
img.src = imageDataUrl;
}
};
reader.onerror = () => showToast("Error reading image file.", 'error');
reader.readAsDataURL(file);
if(event.target) event.target.value = '';
}
}, [canvasWidth, canvasHeight, updateCanvasState, showToast]);
const handleExportImage = useCallback(() => {
if (currentDataURL) {
const link = document.createElement('a');
link.href = currentDataURL;
link.download = `paint-masterpiece-${Date.now()}.png`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
showToast('Image exported!', 'success');
} else {
showToast('No image to export.', 'info');
}
}, [currentDataURL, showToast]);
const handleClearCanvas = useCallback(() => {
const blankCanvas = getBlankCanvasDataURL(canvasWidth, canvasHeight);
updateCanvasState(blankCanvas, canvasWidth, canvasHeight);
showToast("Canvas cleared.", "info");
}, [canvasWidth, canvasHeight, updateCanvasState, showToast]);
const handleCanvasSizeChange = useCallback((newWidth: number, newHeight: number) => {
if (newWidth === canvasWidth && newHeight === canvasHeight) {
return;
}
requestConfirmation(
"Confirm Canvas Size Change",
"Changing canvas size will clear the current drawing and history. Are you sure you want to continue?",
() => {
const blankCanvas = getBlankCanvasDataURL(newWidth, newHeight);
updateCanvasState(blankCanvas, newWidth, newHeight);
setZoomLevel(0.5);
showToast(`Canvas resized to ${newWidth}x${newHeight}px. Drawing cleared.`, 'info');
},
{ isDestructive: true }
);
}, [canvasWidth, canvasHeight, requestConfirmation, updateCanvasState, setZoomLevel, showToast]);
return { handleLoadImageFile, handleExportImage, handleClearCanvas, handleCanvasSizeChange };
};