import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState, } from 'react'; import { FacingMode, WebCameraHandler, WebCameraProps } from './camera.types'; export const WebCamera = forwardRef( ({ active, videoStyle, onError }, ref) => { const captureQuality = 0.8; const captureType = 'jpeg'; const videoRef = useRef(null); const canvasRef = useRef(null); const streamRef = useRef(null); const [facingMode, setFacingMode] = useState('environment'); useEffect(() => { if (!active) { stopCamera(); return; } let mounted = true; const startCamera = async () => { try { const stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: { ideal: facingMode } }, }); if (!mounted || !videoRef.current) { stream.getTracks().forEach((t) => t.stop()); return; } videoRef.current.srcObject = stream; await videoRef.current.play(); streamRef.current = stream; } catch (err) { onError?.(err as Error); } }; startCamera(); return () => { mounted = false; stopCamera(); }; }, [active, facingMode]); const stopCamera = useCallback(() => { streamRef.current?.getTracks().forEach((t) => t.stop()); streamRef.current = null; if (videoRef.current) { videoRef.current.pause(); videoRef.current.srcObject = null; } }, []); const capture = useCallback(async (): Promise => { if (!videoRef.current || !canvasRef.current) return null; const video = videoRef.current; const canvas = canvasRef.current; const ctx = canvas.getContext('2d'); if (!ctx || video.readyState < 2) return null; canvas.width = video.videoWidth; canvas.height = video.videoHeight; ctx.drawImage(video, 0, 0); return new Promise((resolve) => { canvas.toBlob( (blob) => { if (!blob) return resolve(null); resolve( new File([blob], `capture-${Date.now()}.${captureType}`, { type: `image/${captureType}`, }), ); }, `image/${captureType}`, captureQuality, ); }); }, [captureType, captureQuality]); const switchCamera = useCallback(() => { setFacingMode((p) => (p === 'user' ? 'environment' : 'user')); }, []); useImperativeHandle(ref, () => ({ capture, switchCamera }), [ capture, switchCamera, ]); return (
); }, );