Skip to content

Instantly share code, notes, and snippets.

@sneznaovca
Created February 28, 2020 01:58
Show Gist options
  • Save sneznaovca/9162cba070346fe6ab838556136872f0 to your computer and use it in GitHub Desktop.
Save sneznaovca/9162cba070346fe6ab838556136872f0 to your computer and use it in GitHub Desktop.
import React, {useCallback, useLayoutEffect} from 'react';
import Quagga from '@ericblade/quagga2';
import QrCodeReader from '@ericblade/quagga2-reader-qr';
Quagga.registerReader('qrcode', QrCodeReader);
function getMedian(arr) {
arr.sort((a, b) => a - b);
const half = Math.floor(arr.length / 2);
if (arr.length % 2 === 1) {
return arr[half];
}
return (arr[half - 1] + arr[half]) / 2;
}
function getMedianOfCodeErrors(decodedCodes) {
const errors = decodedCodes.filter(x => x.error !== undefined).map(x => x.error);
const medianOfErrors = getMedian(errors);
return medianOfErrors;
}
const defaultConstraints = {
width: 640,
height: 480,
};
const defaultLocatorSettings = {
patchSize: 'medium',
halfSample: true,
};
const validateBarcode = (code) => {
if (code.length != 11) {
return false;
}
return true;
};
const defaultDecoders = ['code_128_reader', 'qrcode'];
const ScannerCore = ({
onDetected,
scannerRef,
onScannerReady,
cameraId,
facingMode,
constraints = defaultConstraints,
locator = defaultLocatorSettings,
numOfWorkers = navigator.hardwareConcurrency || 0,
decoders = defaultDecoders,
locate = true,
}) => {
const errorCheck = useCallback((result) => {
if (!onDetected) {
return;
}
const err = getMedianOfCodeErrors(result.codeResult.decodedCodes);
// if Quagga is at least 75% certain that it read correctly, then accept the code.
if (err < 0.25) {
onDetected(result);
}
}, [onDetected]);
const handleProcessed = (result) => {
const drawingCtx = Quagga.canvas.ctx.overlay;
const drawingCanvas = Quagga.canvas.dom.overlay;
drawingCtx.font = "24px Arial";
drawingCtx.fillStyle = 'green';
if (result) {
if (result.boxes) {
drawingCtx.clearRect(0, 0, parseInt(drawingCanvas.getAttribute('width')), parseInt(drawingCanvas.getAttribute('height')));
result.boxes.filter((box) => box !== result.box).forEach((box) => {
Quagga.ImageDebug.drawPath(box, {x: 0, y: 1}, drawingCtx, {color: 'purple', lineWidth: 2});
});
}
if (result.box) {
Quagga.ImageDebug.drawPath(result.box, {x: 0, y: 1}, drawingCtx, {color: 'blue', lineWidth: 2});
}
if (result.codeResult && result.codeResult.code) {
const validated = validateBarcode(result.codeResult.code);
Quagga.ImageDebug.drawPath(result.line, {
x: 'x',
y: 'y'
}, drawingCtx, {color: validated ? 'green' : 'red', lineWidth: 3});
drawingCtx.font = "24px Arial";
drawingCtx.fillStyle = validated ? 'green' : 'red';
drawingCtx.fillText(`${result.codeResult.code} valid: ${validated}`, 10, 50);
drawingCtx.fillText(result.codeResult.code, 10, 20);
if (validated) {
onDetected(result);
}
}
}
};
useLayoutEffect(() => {
Quagga.init({
inputStream: {
type: 'LiveStream',
constraints: {
...constraints,
...(cameraId && {deviceId: cameraId}),
...(!cameraId && {facingMode}),
},
target: scannerRef.current,
},
locator,
numOfWorkers,
readers: decoders,
locate,
}, (err) => {
Quagga.onProcessed(handleProcessed);
if (err) {
return console.log('Error starting Quagga:', err);
}
if (scannerRef && scannerRef.current) {
Quagga.start();
if (onScannerReady) {
onScannerReady();
}
}
});
Quagga.onDetected(errorCheck);
return () => {
Quagga.offDetected(errorCheck);
Quagga.offProcessed(handleProcessed);
Quagga.stop();
};
}, [cameraId, onDetected, onScannerReady, scannerRef, errorCheck, constraints, locator, decoders, locate]);
return null;
}
export default ScannerCore;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment