Tina Tarighian
commited on
Commit
·
5f82b57
1
Parent(s):
c5e133c
mobile ratio
Browse files- components/CameraSetup.js +40 -26
- components/MainContent.js +1 -4
- hooks/useDeviceAndCanvas.js +24 -6
components/CameraSetup.js
CHANGED
|
@@ -16,6 +16,20 @@ const CameraSetup = ({
|
|
| 16 |
// Track if camera is currently being set up
|
| 17 |
const isSettingUpCamera = useRef(false);
|
| 18 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
// Set up the webcam
|
| 20 |
useEffect(() => {
|
| 21 |
// Set mounted flag
|
|
@@ -35,17 +49,24 @@ const CameraSetup = ({
|
|
| 35 |
tracks.forEach(track => track.stop());
|
| 36 |
}
|
| 37 |
|
| 38 |
-
// Get camera
|
| 39 |
-
const
|
| 40 |
-
video: {
|
| 41 |
facingMode: facingMode,
|
| 42 |
-
|
| 43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
},
|
| 45 |
audio: false
|
| 46 |
-
}
|
|
|
|
|
|
|
| 47 |
|
| 48 |
-
// Check if component is still mounted before continuing
|
| 49 |
if (!isMounted.current) {
|
| 50 |
stream.getTracks().forEach(track => track.stop());
|
| 51 |
return;
|
|
@@ -53,30 +74,20 @@ const CameraSetup = ({
|
|
| 53 |
|
| 54 |
videoRef.current.srcObject = stream;
|
| 55 |
|
| 56 |
-
//
|
|
|
|
|
|
|
| 57 |
try {
|
| 58 |
await videoRef.current.play();
|
|
|
|
|
|
|
| 59 |
} catch (playError) {
|
| 60 |
console.log("Play interrupted, this is normal if component remounted:", playError);
|
| 61 |
-
// Don't treat play interruptions as fatal errors
|
| 62 |
if (playError.name !== "AbortError") {
|
| 63 |
throw playError;
|
| 64 |
}
|
| 65 |
}
|
| 66 |
|
| 67 |
-
// Get the actual video dimensions once metadata is loaded
|
| 68 |
-
videoRef.current.onloadedmetadata = () => {
|
| 69 |
-
if (!isMounted.current) return;
|
| 70 |
-
|
| 71 |
-
const videoWidth = videoRef.current.videoWidth;
|
| 72 |
-
const videoHeight = videoRef.current.videoHeight;
|
| 73 |
-
const aspectRatio = videoWidth / videoHeight;
|
| 74 |
-
setVideoAspectRatio(aspectRatio);
|
| 75 |
-
|
| 76 |
-
// Update canvas size with the correct aspect ratio
|
| 77 |
-
updateCanvasSize(aspectRatio);
|
| 78 |
-
};
|
| 79 |
-
|
| 80 |
if (isMounted.current) {
|
| 81 |
setCameraError(false);
|
| 82 |
console.log("Camera set up successfully");
|
|
@@ -98,12 +109,15 @@ const CameraSetup = ({
|
|
| 98 |
// Set mounted flag to false to prevent state updates after unmount
|
| 99 |
isMounted.current = false;
|
| 100 |
|
| 101 |
-
if (videoRef.current
|
| 102 |
-
|
| 103 |
-
|
|
|
|
|
|
|
|
|
|
| 104 |
}
|
| 105 |
};
|
| 106 |
-
}, [videoRef, canvasRef, containerRef, facingMode
|
| 107 |
|
| 108 |
// Function to switch camera
|
| 109 |
const switchCamera = async () => {
|
|
|
|
| 16 |
// Track if camera is currently being set up
|
| 17 |
const isSettingUpCamera = useRef(false);
|
| 18 |
|
| 19 |
+
const updateDimensions = () => {
|
| 20 |
+
if (!videoRef.current || !isMounted.current) return;
|
| 21 |
+
|
| 22 |
+
const videoWidth = videoRef.current.videoWidth;
|
| 23 |
+
const videoHeight = videoRef.current.videoHeight;
|
| 24 |
+
|
| 25 |
+
// Only update if we have valid dimensions
|
| 26 |
+
if (videoWidth && videoHeight) {
|
| 27 |
+
const aspectRatio = videoWidth / videoHeight;
|
| 28 |
+
setVideoAspectRatio(aspectRatio);
|
| 29 |
+
updateCanvasSize(aspectRatio);
|
| 30 |
+
}
|
| 31 |
+
};
|
| 32 |
+
|
| 33 |
// Set up the webcam
|
| 34 |
useEffect(() => {
|
| 35 |
// Set mounted flag
|
|
|
|
| 49 |
tracks.forEach(track => track.stop());
|
| 50 |
}
|
| 51 |
|
| 52 |
+
// Get camera constraints based on device
|
| 53 |
+
const constraints = {
|
| 54 |
+
video: {
|
| 55 |
facingMode: facingMode,
|
| 56 |
+
// For mobile, prioritize height to prevent letterboxing
|
| 57 |
+
...(isMobile ? {
|
| 58 |
+
height: { ideal: 1080, min: 720 },
|
| 59 |
+
width: { ideal: 1920, min: 1280 }
|
| 60 |
+
} : {
|
| 61 |
+
width: { ideal: 1920 },
|
| 62 |
+
height: { ideal: 1080 }
|
| 63 |
+
})
|
| 64 |
},
|
| 65 |
audio: false
|
| 66 |
+
};
|
| 67 |
+
|
| 68 |
+
const stream = await navigator.mediaDevices.getUserMedia(constraints);
|
| 69 |
|
|
|
|
| 70 |
if (!isMounted.current) {
|
| 71 |
stream.getTracks().forEach(track => track.stop());
|
| 72 |
return;
|
|
|
|
| 74 |
|
| 75 |
videoRef.current.srcObject = stream;
|
| 76 |
|
| 77 |
+
// Add loadeddata event listener for more reliable dimension detection
|
| 78 |
+
videoRef.current.addEventListener('loadeddata', updateDimensions);
|
| 79 |
+
|
| 80 |
try {
|
| 81 |
await videoRef.current.play();
|
| 82 |
+
// Double check dimensions after a short delay to ensure they're correct
|
| 83 |
+
setTimeout(updateDimensions, 100);
|
| 84 |
} catch (playError) {
|
| 85 |
console.log("Play interrupted, this is normal if component remounted:", playError);
|
|
|
|
| 86 |
if (playError.name !== "AbortError") {
|
| 87 |
throw playError;
|
| 88 |
}
|
| 89 |
}
|
| 90 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
if (isMounted.current) {
|
| 92 |
setCameraError(false);
|
| 93 |
console.log("Camera set up successfully");
|
|
|
|
| 109 |
// Set mounted flag to false to prevent state updates after unmount
|
| 110 |
isMounted.current = false;
|
| 111 |
|
| 112 |
+
if (videoRef.current) {
|
| 113 |
+
videoRef.current.removeEventListener('loadeddata', updateDimensions);
|
| 114 |
+
if (videoRef.current.srcObject) {
|
| 115 |
+
const tracks = videoRef.current.srcObject.getTracks();
|
| 116 |
+
tracks.forEach(track => track.stop());
|
| 117 |
+
}
|
| 118 |
}
|
| 119 |
};
|
| 120 |
+
}, [videoRef, canvasRef, containerRef, facingMode]);
|
| 121 |
|
| 122 |
// Function to switch camera
|
| 123 |
const switchCamera = async () => {
|
components/MainContent.js
CHANGED
|
@@ -63,10 +63,7 @@ const MainContent = ({
|
|
| 63 |
<canvas
|
| 64 |
ref={canvasRef}
|
| 65 |
className="rounded-lg shadow-lg w-full"
|
| 66 |
-
style={{
|
| 67 |
-
height: `${canvasHeight}px`,
|
| 68 |
-
...(isMobile && { margin: '0 auto', display: 'block' })
|
| 69 |
-
}}
|
| 70 |
width={canvasWidth}
|
| 71 |
height={canvasHeight}
|
| 72 |
/>
|
|
|
|
| 63 |
<canvas
|
| 64 |
ref={canvasRef}
|
| 65 |
className="rounded-lg shadow-lg w-full"
|
| 66 |
+
style={{ height: `${canvasHeight}px` }}
|
|
|
|
|
|
|
|
|
|
| 67 |
width={canvasWidth}
|
| 68 |
height={canvasHeight}
|
| 69 |
/>
|
hooks/useDeviceAndCanvas.js
CHANGED
|
@@ -43,14 +43,32 @@ const useDeviceAndCanvas = () => {
|
|
| 43 |
if (!isComponentMounted.current) return;
|
| 44 |
|
| 45 |
const containerWidth = document.querySelector('.canvas-container')?.clientWidth || window.innerWidth;
|
| 46 |
-
|
|
|
|
|
|
|
| 47 |
const maxWidth = Math.min(containerWidth, isMobile ? 640 : 960);
|
| 48 |
-
//
|
| 49 |
-
const
|
| 50 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
|
| 52 |
-
|
| 53 |
-
|
|
|
|
| 54 |
}, [isMobile]);
|
| 55 |
|
| 56 |
return {
|
|
|
|
| 43 |
if (!isComponentMounted.current) return;
|
| 44 |
|
| 45 |
const containerWidth = document.querySelector('.canvas-container')?.clientWidth || window.innerWidth;
|
| 46 |
+
const windowHeight = window.innerHeight;
|
| 47 |
+
|
| 48 |
+
// Set maximum dimensions for the canvas
|
| 49 |
const maxWidth = Math.min(containerWidth, isMobile ? 640 : 960);
|
| 50 |
+
// For mobile, limit height to 70% of viewport height to prevent excessive scrolling
|
| 51 |
+
const maxHeight = isMobile ? windowHeight * 0.7 : windowHeight * 0.8;
|
| 52 |
+
|
| 53 |
+
// Calculate dimensions maintaining aspect ratio within constraints
|
| 54 |
+
let width = maxWidth;
|
| 55 |
+
let height = width / aspectRatio;
|
| 56 |
+
|
| 57 |
+
// If height exceeds max height, recalculate width to maintain aspect ratio
|
| 58 |
+
if (height > maxHeight) {
|
| 59 |
+
height = maxHeight;
|
| 60 |
+
width = height * aspectRatio;
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
// Ensure width doesn't exceed container
|
| 64 |
+
if (width > containerWidth) {
|
| 65 |
+
width = containerWidth;
|
| 66 |
+
height = width / aspectRatio;
|
| 67 |
+
}
|
| 68 |
|
| 69 |
+
// Round dimensions to prevent subpixel rendering issues
|
| 70 |
+
setCanvasWidth(Math.round(width));
|
| 71 |
+
setCanvasHeight(Math.round(height));
|
| 72 |
}, [isMobile]);
|
| 73 |
|
| 74 |
return {
|