| 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); |
| |
| const drawX = (finalCanvasWidth - imageToDrawWidth) / 2; |
| const drawY = (finalCanvasHeight - imageToDrawHeight) / 2; |
| |
| tempCtx.drawImage(img, drawX, drawY, imageToDrawWidth, imageToDrawHeight); |
| const compositeDataURL = tempCanvas.toDataURL('image/png'); |
| |
| updateCanvasState(compositeDataURL, finalCanvasWidth, finalCanvasHeight); |
| setZoomLevel(0.5); |
| 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, setZoomLevel]); |
| |
| 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(() => { |
| requestConfirmation( |
| "Clear Canvas", |
| "Are you sure you want to clear the entire canvas? This action cannot be undone from history.", |
| () => { |
| const blankCanvas = getBlankCanvasDataURL(canvasWidth, canvasHeight); |
| updateCanvasState(blankCanvas, canvasWidth, canvasHeight); |
| setZoomLevel(0.5); |
| showToast("Canvas cleared.", "info"); |
| }, |
| { isDestructive: true } |
| ); |
| }, [requestConfirmation, canvasWidth, canvasHeight, updateCanvasState, setZoomLevel, 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 }; |
| }; |
|
|