-
-
Notifications
You must be signed in to change notification settings - Fork 11
/
useBarcodeScanner.ts
128 lines (117 loc) · 4.25 KB
/
useBarcodeScanner.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import { useState } from "react";
import { type ViewProps } from "react-native";
import {
runAtTargetFps,
useFrameProcessor,
type CameraProps,
type Frame,
} from "react-native-vision-camera";
import { Worklets, useSharedValue } from "react-native-worklets-core";
import { ScanBarcodesOptions, scanCodes } from "src/module";
import type { Barcode, BarcodeType, Highlight, Rect, Size } from "src/types";
import { computeHighlights } from "..";
import { useLatestSharedValue } from "./useLatestSharedValue";
type ResizeMode = NonNullable<CameraProps["resizeMode"]>;
export type UseBarcodeScannerOptions = {
barcodeTypes?: BarcodeType[];
regionOfInterest?: Rect;
fps?: number;
onBarcodeScanned: (barcodes: Barcode[], frame: Frame) => void;
disableHighlighting?: boolean;
resizeMode?: ResizeMode;
scanMode?: "continuous" | "once";
isMountedRef?: { value: boolean };
};
export const useBarcodeScanner = ({
barcodeTypes,
regionOfInterest,
onBarcodeScanned,
disableHighlighting,
resizeMode = "cover",
scanMode = "continuous",
isMountedRef,
fps = 5,
}: UseBarcodeScannerOptions) => {
// Layout of the <Camera /> component
const layoutRef = useSharedValue<Size>({ width: 0, height: 0 });
const onLayout: ViewProps["onLayout"] = (event) => {
const { width, height } = event.nativeEvent.layout;
layoutRef.value = { width, height };
};
const resizeModeRef = useLatestSharedValue<ResizeMode>(resizeMode);
const isPristineRef = useSharedValue<boolean>(true);
// Barcode highlights related state
const barcodesRef = useSharedValue<Barcode[]>([]);
// Barcode highlights related state
const [highlights, setHighlights] = useState<Highlight[]>([]);
const lastHighlightsCount = useSharedValue<number>(0);
const setHighlightsJS = Worklets.createRunOnJS(setHighlights);
const frameProcessor = useFrameProcessor(
(frame) => {
"worklet";
if (isMountedRef && isMountedRef.value === false) {
return;
}
runAtTargetFps(fps, () => {
"worklet";
const { value: layout } = layoutRef;
const { value: prevBarcodes } = barcodesRef;
const { value: resizeMode } = resizeModeRef;
const { width, height, orientation } = frame;
// Call the native barcode scanner
const options: ScanBarcodesOptions = {};
if (barcodeTypes !== undefined) {
options.barcodeTypes = barcodeTypes;
}
if (regionOfInterest !== undefined) {
const { x, y, width, height } = regionOfInterest;
options.regionOfInterest = [x, y, width, height];
}
const barcodes = scanCodes(frame, options);
if (barcodes.length > 0) {
// If the scanMode is "continuous", we stream all the barcodes responses
if (scanMode === "continuous") {
onBarcodeScanned(barcodes, frame);
// If the scanMode is "once", we only call the callback if the barcodes have actually changed
} else if (scanMode === "once") {
const hasChanged =
prevBarcodes.length !== barcodes.length ||
JSON.stringify(prevBarcodes.map(({ value }) => value)) !==
JSON.stringify(barcodes.map(({ value }) => value));
if (hasChanged) {
onBarcodeScanned(barcodes, frame);
}
}
barcodesRef.value = barcodes;
}
if (disableHighlighting !== true && resizeMode !== undefined) {
// We must ignore the first frame because as it has width/height inverted (maybe the right value though?)
if (isPristineRef.value) {
isPristineRef.value = false;
return;
}
const highlights = computeHighlights(
barcodes,
{ width, height, orientation }, // "serialized" frame
layout,
resizeMode,
);
// Spare a re-render if the highlights are both empty
if (lastHighlightsCount.value === 0 && highlights.length === 0) {
return;
}
lastHighlightsCount.value = highlights.length;
setHighlightsJS(highlights);
}
});
},
[layoutRef, resizeModeRef, disableHighlighting],
);
return {
props: {
frameProcessor,
onLayout,
},
highlights,
};
};