move to git.n39.euall books in the bag ...
This commit is contained in:
commit
8cc2662092
64 changed files with 134252 additions and 0 deletions
frontend/src/shared/utils
184
frontend/src/shared/utils/useScanner.ts
Normal file
184
frontend/src/shared/utils/useScanner.ts
Normal file
|
@ -0,0 +1,184 @@
|
|||
import Quagga, {
|
||||
QuaggaJSCodeReader,
|
||||
QuaggaJSResultObject,
|
||||
} from "@ericblade/quagga2";
|
||||
import { useState, useCallback, useEffect } from "react";
|
||||
|
||||
const constraints = {
|
||||
width: 640,
|
||||
height: 480,
|
||||
};
|
||||
|
||||
const locator = {
|
||||
patchSize: "medium",
|
||||
halfSample: true,
|
||||
willReadFrequently: true,
|
||||
};
|
||||
|
||||
const readers: QuaggaJSCodeReader[] = ["ean_reader"];
|
||||
|
||||
export const useScanner = ({
|
||||
onDetected,
|
||||
}: {
|
||||
onDetected?: (result: string) => void;
|
||||
}): {
|
||||
setScannerRef: (ref: HTMLDivElement | null) => void;
|
||||
scannerError: string | undefined;
|
||||
} => {
|
||||
const [scannerError, setScannerError] = useState<string | undefined>();
|
||||
|
||||
const [scannerRef, setScannerRef] = useState<HTMLDivElement | null>(null);
|
||||
|
||||
const getMedian = useCallback((arr: (number | undefined)[]) => {
|
||||
const newArr = [...arr].filter((x) => typeof x !== "undefined");
|
||||
if (!newArr.length) {
|
||||
return;
|
||||
}
|
||||
newArr.sort((a, b) => a! - b!);
|
||||
const half = Math.floor(newArr.length / 2);
|
||||
if (newArr.length % 2 === 1) {
|
||||
return newArr[half];
|
||||
}
|
||||
return (newArr[half - 1]! + newArr[half]!) / 2;
|
||||
}, []);
|
||||
|
||||
const getMedianOfCodeErrors = useCallback(
|
||||
(result: QuaggaJSResultObject) => {
|
||||
const errors = result.codeResult.decodedCodes.flatMap((x) => x.error);
|
||||
const medianOfErrors = getMedian(errors);
|
||||
return medianOfErrors;
|
||||
},
|
||||
[getMedian]
|
||||
);
|
||||
|
||||
const errorCheck = useCallback(
|
||||
(result: QuaggaJSResultObject) => {
|
||||
if (!onDetected) {
|
||||
return;
|
||||
}
|
||||
const err = getMedianOfCodeErrors(result);
|
||||
|
||||
if (!err) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (err < 0.1 && result.codeResult.code) {
|
||||
onDetected(result.codeResult.code);
|
||||
}
|
||||
},
|
||||
[getMedianOfCodeErrors, onDetected]
|
||||
);
|
||||
|
||||
const handleProcessed = (result: QuaggaJSResultObject) => {
|
||||
const drawingCtx = Quagga.canvas.ctx.overlay;
|
||||
const drawingCanvas = Quagga.canvas.dom.overlay;
|
||||
drawingCtx.font = "24px Arial";
|
||||
drawingCtx.fillStyle = "green";
|
||||
const width = drawingCanvas.getAttribute("width");
|
||||
const height = drawingCanvas.getAttribute("height");
|
||||
|
||||
if (result) {
|
||||
if (result.boxes && width && height) {
|
||||
drawingCtx.clearRect(0, 0, parseInt(width), parseInt(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) {
|
||||
drawingCtx.font = "24px Arial";
|
||||
drawingCtx.fillText(result.codeResult.code, 10, 20);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getCameraId = useCallback(async () => {
|
||||
await navigator.mediaDevices.getUserMedia({
|
||||
video: true,
|
||||
});
|
||||
|
||||
const devices = await navigator.mediaDevices
|
||||
.enumerateDevices()
|
||||
.then((ds) => ds.filter((d) => d.kind === "videoinput"));
|
||||
|
||||
const back = devices.filter((d) => d.label === "Back Camera")[0];
|
||||
|
||||
return !back ? devices[0].deviceId : back.deviceId;
|
||||
}, []);
|
||||
|
||||
const initQuagga = useCallback(async () => {
|
||||
try {
|
||||
const cameraId = await getCameraId();
|
||||
|
||||
if (!scannerRef || !cameraId) {
|
||||
console.log("fehlt", scannerRef, cameraId);
|
||||
return;
|
||||
}
|
||||
await Quagga.init(
|
||||
{
|
||||
inputStream: {
|
||||
type: "LiveStream",
|
||||
constraints: {
|
||||
...constraints,
|
||||
...(cameraId && { deviceId: cameraId }),
|
||||
},
|
||||
target: scannerRef,
|
||||
willReadFrequently: true,
|
||||
},
|
||||
locator,
|
||||
decoder: { readers },
|
||||
locate: true,
|
||||
},
|
||||
async (err) => {
|
||||
Quagga.onProcessed(handleProcessed);
|
||||
|
||||
if (err) {
|
||||
return console.error("Error starting Quagga:", err);
|
||||
}
|
||||
if (scannerRef) {
|
||||
Quagga.start();
|
||||
}
|
||||
}
|
||||
);
|
||||
Quagga.onDetected(errorCheck);
|
||||
} catch (e: unknown) {
|
||||
setScannerError(JSON.stringify(e));
|
||||
}
|
||||
}, [errorCheck, getCameraId, scannerRef]);
|
||||
|
||||
const stopQuagga = useCallback(async () => {
|
||||
await Quagga.CameraAccess.release();
|
||||
await Quagga.stop();
|
||||
Quagga.offDetected(errorCheck);
|
||||
Quagga.offProcessed(handleProcessed);
|
||||
}, [errorCheck]);
|
||||
|
||||
useEffect(() => {
|
||||
const init = () => {
|
||||
initQuagga();
|
||||
};
|
||||
const disable = () => {
|
||||
stopQuagga();
|
||||
};
|
||||
|
||||
if (scannerRef) {
|
||||
init();
|
||||
}
|
||||
return disable();
|
||||
}, [stopQuagga, initQuagga, scannerRef]);
|
||||
|
||||
return {
|
||||
setScannerRef,
|
||||
scannerError,
|
||||
};
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue