@thewirv/react-barcode-scanner
Version:
A React component for scanning QR codes and other barcodes via webcam
119 lines (114 loc) • 4.46 kB
JavaScript
import { jsxs, jsx } from 'react/jsx-runtime';
import { useState, useMemo, useRef, useEffect } from 'react';
import { BrowserMultiFormatReader } from '@zxing/browser';
import { FiCameraOff } from 'react-icons/fi';
import { NotFoundException, ChecksumException, FormatException } from '@zxing/library';
async function decodeBarcodeFromConstraints(codeReader, videoElement, { constraints, onSuccess, onError, }) {
if (!videoElement.current)
return;
try {
const result = await codeReader.decodeOnceFromConstraints({ audio: false, video: constraints, preferCurrentTab: true }, videoElement.current);
onSuccess(result.getText());
}
catch (error) {
if (error &&
onError &&
!(error instanceof NotFoundException ||
error instanceof ChecksumException ||
error instanceof FormatException)) {
onError(error);
}
}
}
const styles = {
barcodeScannerError: {
border: '8px #eee solid',
borderRadius: '10px',
padding: '2rem',
},
barcodeScannerErrorSvg: {
width: '75%',
height: '75%',
display: 'block',
opacity: 0.2,
margin: '0 auto',
},
container: {
width: '100%',
paddingTop: '100%',
overflow: 'hidden',
position: 'relative',
display: 'none',
},
barcodeScannerVisible: {
display: 'block',
},
video: {
top: 0,
left: 0,
width: '100%',
height: '100%',
display: 'block',
overflow: 'hidden',
position: 'absolute',
transform: undefined,
},
};
const BarcodeScanner = ({ doScan = true, constraints = { facingMode: 'environment' }, onSuccess, onError, onLoad, Viewfinder, containerStyle, videoContainerStyle, videoStyle, videoProps: passedVideoProps, }) => {
const [isCameraInitialized, setIsCameraInitialized] = useState(false);
const codeReader = useMemo(() => new BrowserMultiFormatReader(), []);
const videoElement = useRef(null);
const isShowingDisabledImage = !isCameraInitialized || !doScan;
useEffect(() => {
if (!doScan || !onSuccess || !onError) {
return;
}
if (!navigator?.mediaDevices) {
const message = 'Your browser has no support for the MediaDevices API. You could fix this by running "npm i webrtc-adapter"';
console.warn(`[ReactBarcodeScanner]: ${message}`);
onError(new Error(message));
return;
}
decodeBarcodeFromConstraints(codeReader, videoElement, {
constraints,
onSuccess,
onError,
});
}, [onSuccess, onError, doScan, codeReader, constraints]);
const videoProps = useMemo(() => {
function onLoadedData({ nativeEvent, }) {
const eventTarget = nativeEvent.target;
if (!eventTarget?.readyState)
return;
if (eventTarget.readyState === eventTarget.HAVE_ENOUGH_DATA) {
setIsCameraInitialized(true);
if (onLoad) {
onLoad();
}
}
}
const defaultVideoProps = {
playsInline: true,
disablePictureInPicture: true,
muted: true,
onLoadedData: onLoadedData,
style: {
...styles.video,
...videoStyle,
transform: `${videoStyle?.transform ?? ''} ${constraints.facingMode === 'user' ? 'scaleX(-1)' : ''}`,
},
};
if (!passedVideoProps)
return defaultVideoProps;
if (typeof passedVideoProps !== 'function')
return passedVideoProps;
return passedVideoProps(defaultVideoProps);
}, [passedVideoProps, onLoad]);
return (jsxs("section", { style: containerStyle, children: [isShowingDisabledImage && (jsx("div", { style: styles.barcodeScannerError, children: jsx(FiCameraOff, { size: 300, style: styles.barcodeScannerErrorSvg }) })), jsxs("div", { style: {
...styles.container,
...(!isShowingDisabledImage ? styles.barcodeScannerVisible : {}),
...videoContainerStyle,
}, children: [jsx("video", { ref: videoElement, ...videoProps }), !!Viewfinder && jsx(Viewfinder, {})] })] }));
};
BarcodeScanner.displayName = 'BarcodeScanner';
export { BarcodeScanner };