From a1153b146cece0f231fca72ec61d5c018ca7533a Mon Sep 17 00:00:00 2001 From: Jurriaan Barkey Wolf Date: Wed, 29 Jan 2025 17:56:12 +0100 Subject: [PATCH 1/4] getScreenshotBlob function --- src/react-webcam.tsx | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/react-webcam.tsx b/src/react-webcam.tsx index 5b054ca..ff4fc5c 100644 --- a/src/react-webcam.tsx +++ b/src/react-webcam.tsx @@ -50,6 +50,7 @@ interface ScreenshotDimensions { interface ChildrenProps { getScreenshot: (screenshotDimensions?: ScreenshotDimensions) => string | null; + getScreenshotBlob: (screenshotDimensions?: ScreenshotDimensions) => Promise; } export type WebcamProps = Omit, "ref"> & { @@ -204,6 +205,25 @@ export default class Webcam extends React.Component { ); } + getScreenshotBlob(screenshotDimensions?: ScreenshotDimensions): Promise { + const { state, props } = this; + + if (!state.hasUserMedia) return Promise.resolve(null); + + const canvas = this.getCanvas(screenshotDimensions); + if (!canvas) return Promise.resolve(null); + + return new Promise((resolve) => { + canvas.toBlob( + (blob) => { + resolve(blob); + }, + props.screenshotFormat, + props.screenshotQuality + ); + }); + } + getCanvas(screenshotDimensions?: ScreenshotDimensions) { const { state, props } = this; @@ -404,6 +424,7 @@ export default class Webcam extends React.Component { const childrenProps: ChildrenProps = { getScreenshot: this.getScreenshot.bind(this), + getScreenshotBlob: this.getScreenshotBlob.bind(this), }; return ( From 4ea220dd590252257bbc4dff1fe1c0a041d31ce6 Mon Sep 17 00:00:00 2001 From: Jurriaan Barkey Wolf Date: Wed, 29 Jan 2025 17:57:27 +0100 Subject: [PATCH 2/4] Error handling --- src/react-webcam.tsx | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/react-webcam.tsx b/src/react-webcam.tsx index ff4fc5c..716b065 100644 --- a/src/react-webcam.tsx +++ b/src/react-webcam.tsx @@ -213,14 +213,26 @@ export default class Webcam extends React.Component { const canvas = this.getCanvas(screenshotDimensions); if (!canvas) return Promise.resolve(null); - return new Promise((resolve) => { - canvas.toBlob( - (blob) => { - resolve(blob); - }, - props.screenshotFormat, - props.screenshotQuality - ); + if (typeof canvas.toBlob !== 'function') { + return Promise.reject(new Error('Canvas toBlob is not supported')); + } + + return new Promise((resolve, reject) => { + try { + canvas.toBlob( + (blob) => { + if (!blob) { + reject(new Error('Failed to convert canvas to blob')); + return; + } + resolve(blob); + }, + props.screenshotFormat, + props.screenshotQuality + ); + } catch (error) { + reject(error instanceof Error ? error : new Error('Failed to capture screenshot')); + } }); } From d65727a777df65ad3ea397e2ea38aefa03db4d3d Mon Sep 17 00:00:00 2001 From: Jurriaan Barkey Wolf Date: Wed, 29 Jan 2025 17:59:16 +0100 Subject: [PATCH 3/4] Make async for readability --- src/react-webcam.tsx | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/react-webcam.tsx b/src/react-webcam.tsx index 716b065..e940174 100644 --- a/src/react-webcam.tsx +++ b/src/react-webcam.tsx @@ -205,35 +205,35 @@ export default class Webcam extends React.Component { ); } - getScreenshotBlob(screenshotDimensions?: ScreenshotDimensions): Promise { + async getScreenshotBlob(screenshotDimensions?: ScreenshotDimensions): Promise { const { state, props } = this; - if (!state.hasUserMedia) return Promise.resolve(null); + if (!state.hasUserMedia) return null; const canvas = this.getCanvas(screenshotDimensions); - if (!canvas) return Promise.resolve(null); + if (!canvas) return null; if (typeof canvas.toBlob !== 'function') { - return Promise.reject(new Error('Canvas toBlob is not supported')); + throw new Error('Canvas toBlob is not supported'); } - return new Promise((resolve, reject) => { - try { + try { + const blob = await new Promise((resolve) => { canvas.toBlob( - (blob) => { - if (!blob) { - reject(new Error('Failed to convert canvas to blob')); - return; - } - resolve(blob); - }, + (blob) => resolve(blob), props.screenshotFormat, props.screenshotQuality ); - } catch (error) { - reject(error instanceof Error ? error : new Error('Failed to capture screenshot')); + }); + + if (!blob) { + throw new Error('Failed to convert canvas to blob'); } - }); + + return blob; + } catch (error) { + throw error instanceof Error ? error : new Error('Failed to capture screenshot'); + } } getCanvas(screenshotDimensions?: ScreenshotDimensions) { From 0fee37f2aeced43d54c1e567008c6477c28f7a3f Mon Sep 17 00:00:00 2001 From: Jurriaan Barkey Wolf Date: Wed, 29 Jan 2025 18:48:00 +0100 Subject: [PATCH 4/4] Update docs --- README.md | 50 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 9f7a01e..803f556 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,29 @@ You may also pass in an optional `dimensions` object: getScreenshot({width: 1920, height: 1080}); ``` +`getScreenshotBlob` - Returns a Promise that resolves to a Blob of the current webcam image. This is more efficient than base64 encoding when uploading to a server or processing large images. Example: + +```jsx +const capture = async () => { + try { + const blob = await webcamRef.current.getScreenshotBlob({width: 1920, height: 1080}); + if (blob) { + // Upload to a server + const formData = new FormData(); + formData.append('image', blob); + await fetch('/upload', { method: 'POST', body: formData }); + + // Or create an object URL for display + const url = URL.createObjectURL(blob); + image.src = url; + URL.revokeObjectURL(url); // Clean up when done + } + } catch (error) { + console.error('Failed to capture screenshot:', error); + } +}; +``` + ### The Constraints We can build a constraints object by passing it to the videoConstraints prop. This gets passed into getUserMedia method. Please take a look at the MDN docs to get an understanding how this works. @@ -95,14 +118,23 @@ const WebcamCapture = () => ( width={1280} videoConstraints={videoConstraints} > - {({ getScreenshot }) => ( - + {({ getScreenshot, getScreenshotBlob }) => ( +
+ + +
)} ); @@ -121,7 +153,7 @@ const WebcamCapture = () => { const webcamRef = React.useRef(null); const capture = React.useCallback( () => { - const imageSrc = webcamRef.current.getScreenshot(); + const imageSrc = webcamRef.current.getScreenshot(); // or getScreenshotBlob() }, [webcamRef] );