import React, { useRef, useState, useEffect } from 'react'; type Props = { onSignatureChange: (data: { fullSignature: string | null; initials: string | null }) => void; initialSignatures?: { fullSignature: string | null; initials: string | null }; }; const DrawSignatureCanvas: React.FC = ({ onSignatureChange, initialSignatures }) => { const canvasRef1 = useRef(null); const canvasRef2 = useRef(null); const [isDrawing, setIsDrawing] = useState(false); const [paths1, setPaths1] = useState>>([]); const [paths2, setPaths2] = useState>>([]); const [currentPath, setCurrentPath] = useState>([]); const [activeCanvas, setActiveCanvas] = useState<'full' | 'initials' | null>(null); const [initialLoaded, setInitialLoaded] = useState(false); // Store initial signature images to avoid reloading const [initialImage1, setInitialImage1] = useState(null); const [initialImage2, setInitialImage2] = useState(null); // Setup canvas dimensions when component mounts useEffect(() => { const setupCanvas = () => { if (canvasRef1.current) { const rect1 = canvasRef1.current.getBoundingClientRect(); canvasRef1.current.width = rect1.width; canvasRef1.current.height = rect1.height; } if (canvasRef2.current) { const rect2 = canvasRef2.current.getBoundingClientRect(); canvasRef2.current.width = rect2.width; canvasRef2.current.height = rect2.height; } }; setupCanvas(); // Load initial signatures after canvas setup if (initialSignatures && !initialLoaded) { setTimeout(() => { loadInitialSignatures(); }, 100); } }, []); const loadInitialSignatures = () => { if (!initialSignatures) return; let loadedCount = 0; const totalToLoad = (initialSignatures.fullSignature ? 1 : 0) + (initialSignatures.initials ? 1 : 0); const checkComplete = () => { loadedCount++; if (loadedCount === totalToLoad) { setInitialLoaded(true); } }; // Load full signature if (initialSignatures.fullSignature && canvasRef1.current) { const img1 = new Image(); img1.onload = () => { setInitialImage1(img1); const ctx1 = canvasRef1.current?.getContext('2d'); if (ctx1) { ctx1.clearRect(0, 0, canvasRef1.current!.width, canvasRef1.current!.height); ctx1.drawImage(img1, 0, 0); } checkComplete(); }; img1.src = initialSignatures.fullSignature; } // Load initials if (initialSignatures.initials && canvasRef2.current) { const img2 = new Image(); img2.onload = () => { setInitialImage2(img2); const ctx2 = canvasRef2.current?.getContext('2d'); if (ctx2) { ctx2.clearRect(0, 0, canvasRef2.current!.width, canvasRef2.current!.height); ctx2.drawImage(img2, 0, 0); } checkComplete(); }; img2.src = initialSignatures.initials; } if (totalToLoad === 0) { setInitialLoaded(true); } }; const handleMouseDown = (e: React.MouseEvent) => { setIsDrawing(true); const rect = e.currentTarget.getBoundingClientRect(); const point = { x: e.clientX - rect.left, y: e.clientY - rect.top }; setCurrentPath([point]); setActiveCanvas(e.currentTarget === canvasRef1.current ? 'full' : 'initials'); }; const handleMouseMove = (e: React.MouseEvent) => { if (!isDrawing) return; const rect = e.currentTarget.getBoundingClientRect(); const point = { x: e.clientX - rect.left, y: e.clientY - rect.top }; const newPath = [...currentPath, point]; setCurrentPath(newPath); // Draw immediately for smooth experience drawCurrentPath(e.currentTarget as HTMLCanvasElement, newPath); }; const drawCurrentPath = (canvas: HTMLCanvasElement, path: Array<{ x: number; y: number }>) => { if (!canvas || path.length < 2) return; const ctx = canvas.getContext('2d'); if (!ctx) return; // Only draw the current path segment without clearing the canvas ctx.strokeStyle = '#000'; ctx.lineWidth = 2; ctx.lineJoin = 'round'; ctx.lineCap = 'round'; const lastPoint = path[path.length - 2]; const currentPoint = path[path.length - 1]; ctx.beginPath(); ctx.moveTo(lastPoint.x, lastPoint.y); ctx.lineTo(currentPoint.x, currentPoint.y); ctx.stroke(); }; const exportCanvasAsImage = (canvas: HTMLCanvasElement | null): string | null => { if (!canvas) return null; return canvas.toDataURL('image/png'); }; const handleMouseUp = () => { setIsDrawing(false); if (currentPath.length === 0 || !activeCanvas) return; if (activeCanvas === 'full') { const newPaths = [...paths1, currentPath]; setPaths1(newPaths); } else { const newPaths = [...paths2, currentPath]; setPaths2(newPaths); } setCurrentPath([]); // Export both canvases and notify parent const fullSignature = exportCanvasAsImage(canvasRef1.current); const initials = exportCanvasAsImage(canvasRef2.current); onSignatureChange({ fullSignature, initials }); }; const redrawCanvas = (canvas: HTMLCanvasElement, paths: Array>, initialImage: HTMLImageElement | null) => { const ctx = canvas.getContext('2d'); if (!ctx) return; // Clear canvas ctx.clearRect(0, 0, canvas.width, canvas.height); // Draw initial signature if exists if (initialImage) { ctx.drawImage(initialImage, 0, 0); } // Draw all paths ctx.strokeStyle = '#000'; ctx.lineWidth = 2; ctx.lineJoin = 'round'; ctx.lineCap = 'round'; paths.forEach((path) => { if (path.length < 2) return; ctx.beginPath(); ctx.moveTo(path[0].x, path[0].y); for (let i = 1; i < path.length; i++) { ctx.lineTo(path[i].x, path[i].y); } ctx.stroke(); }); }; const clearCanvas = () => { if (canvasRef1.current) { const ctx1 = canvasRef1.current.getContext('2d'); ctx1?.clearRect(0, 0, canvasRef1.current.width, canvasRef1.current.height); } if (canvasRef2.current) { const ctx2 = canvasRef2.current.getContext('2d'); ctx2?.clearRect(0, 0, canvasRef2.current.width, canvasRef2.current.height); } setPaths1([]); setPaths2([]); setInitialImage1(null); setInitialImage2(null); setInitialLoaded(false); onSignatureChange({ fullSignature: null, initials: null }); }; const undoLastPath = () => { if (activeCanvas === 'full' && paths1.length > 0) { const newPaths = paths1.slice(0, -1); setPaths1(newPaths); redrawCanvas(canvasRef1.current!, newPaths, initialImage1); } else if (activeCanvas === 'initials' && paths2.length > 0) { const newPaths = paths2.slice(0, -1); setPaths2(newPaths); redrawCanvas(canvasRef2.current!, newPaths, initialImage2); } // Update parent with new signature data setTimeout(() => { const fullSignature = exportCanvasAsImage(canvasRef1.current); const initials = exportCanvasAsImage(canvasRef2.current); onSignatureChange({ fullSignature, initials }); }, 10); }; return (

Draw Signature

Create your signature by drawing directly on the canvas

{/* Full Signature Canvas */}
{/* Initials Canvas */}
{/* Controls */}
); }; export default DrawSignatureCanvas;