1
1
import { useEffect , useRef , RefObject } from "react" ;
2
- import { BarcodeFormat , BrowserMultiFormatReader , DecodeHintType , NotFoundException } from "@zxing/library/cjs" ;
2
+ import {
3
+ BarcodeFormat ,
4
+ BinaryBitmap ,
5
+ BrowserMultiFormatReader ,
6
+ DecodeHintType ,
7
+ HTMLCanvasElementLuminanceSource ,
8
+ HybridBinarizer ,
9
+ NotFoundException ,
10
+ Result
11
+ } from "@zxing/library/cjs" ;
3
12
import { useEventCallback } from "@mendix/pluggable-widgets-commons" ;
4
13
5
14
const hints = new Map ( ) ;
@@ -23,19 +32,61 @@ const mediaStreamConstraints: MediaStreamConstraints = {
23
32
type UseReaderHook = ( args : {
24
33
onSuccess ?: ( data : string ) => void ;
25
34
onError ?: ( e : Error ) => void ;
35
+ useCrop : boolean ;
26
36
} ) => RefObject < HTMLVideoElement > ;
27
37
28
38
export const useReader : UseReaderHook = args => {
29
39
const videoRef = useRef < HTMLVideoElement > ( null ) ;
30
40
const onSuccess = useEventCallback ( args . onSuccess ) ;
31
41
const onError = useEventCallback ( args . onError ) ;
42
+ const scale = 0.3 ;
43
+ const stopped = useRef < Boolean > ( false ) ;
44
+ const checkNotFound = ( error : any ) : boolean => {
45
+ const ifNotFound = error instanceof NotFoundException ;
46
+ return ifNotFound && ! stopped . current ;
47
+ } ;
48
+
49
+ const scanWithCropOnce = ( reader : BrowserMultiFormatReader ) : Promise < Result > => {
50
+ const cropWidth = videoRef . current ! . videoWidth * scale ;
51
+ const captureCanvas = reader . createCaptureCanvas ( videoRef . current ! ) ;
52
+ captureCanvas . width = cropWidth ;
53
+ captureCanvas . height = cropWidth ;
54
+ const loop = ( resolve : ( value : Result ) => void , reject : ( reason ?: Error ) => void ) => {
55
+ try {
56
+ const canvasContext = captureCanvas . getContext ( "2d" ) ;
57
+ if ( canvasContext !== null ) {
58
+ canvasContext . drawImage (
59
+ videoRef . current ! ,
60
+ ( videoRef . current ! . videoWidth * ( 1 - scale ) ) / 2 ,
61
+ ( videoRef . current ! . videoHeight - cropWidth ) / 2 ,
62
+ cropWidth ,
63
+ cropWidth ,
64
+ 0 ,
65
+ 0 ,
66
+ captureCanvas . width ,
67
+ captureCanvas . width
68
+ ) ;
69
+ const luminanceSource = new HTMLCanvasElementLuminanceSource ( captureCanvas ) ;
70
+ const binaryBitmap = new BinaryBitmap ( new HybridBinarizer ( luminanceSource ) ) ;
71
+ const result = reader . decodeBitmap ( binaryBitmap ) ;
72
+ resolve ( result ) ;
73
+ }
74
+ } catch ( error ) {
75
+ if ( checkNotFound ( error ) ) {
76
+ setTimeout ( ( ) => loop ( resolve , reject ) , reader . timeBetweenDecodingAttempts ) ;
77
+ } else {
78
+ reject ( error ) ;
79
+ }
80
+ }
81
+ } ;
82
+ return new Promise ( loop ) ;
83
+ } ;
32
84
33
85
useEffect ( ( ) => {
34
- let stopped = false ;
35
- const reader = new BrowserMultiFormatReader ( hints , 2000 ) ;
86
+ const reader = new BrowserMultiFormatReader ( hints , 500 ) ;
36
87
37
88
const stop = ( ) : void => {
38
- stopped = true ;
89
+ stopped . current = true ;
39
90
reader . stopAsyncDecode ( ) ;
40
91
reader . reset ( ) ;
41
92
} ;
@@ -44,16 +95,24 @@ export const useReader: UseReaderHook = args => {
44
95
let stream ;
45
96
try {
46
97
stream = await navigator . mediaDevices . getUserMedia ( mediaStreamConstraints ) ;
47
- if ( ! stopped && videoRef . current ) {
48
- const result = await reader . decodeOnceFromStream ( stream , videoRef . current ) ;
49
- if ( ! stopped ) {
98
+ if ( videoRef . current ) {
99
+ let result : Result ;
100
+ if ( args . useCrop ) {
101
+ videoRef . current . srcObject = stream ;
102
+ videoRef . current . autofocus = true ;
103
+ videoRef . current . playsInline = true ; // Fix error in Safari
104
+ await videoRef . current . play ( ) ;
105
+ result = await scanWithCropOnce ( reader ) ;
106
+ } else {
107
+ result = await reader . decodeOnceFromStream ( stream , videoRef . current ) ;
108
+ }
109
+ if ( ! stopped . current ) {
50
110
onSuccess ( result . getText ( ) ) ;
51
111
}
52
112
}
53
113
} catch ( error ) {
54
114
// Suppress not found error if widget is closed normally (eg. leaving page);
55
- const isNotFound = stopped && error instanceof NotFoundException ;
56
- if ( ! isNotFound ) {
115
+ if ( ! checkNotFound ( error ) ) {
57
116
if ( error instanceof Error ) {
58
117
console . error ( error . message ) ;
59
118
}
0 commit comments