Skip to content

Commit f1e5842

Browse files
authored
[WC-1763]: barcode scanner improvements (#467)
2 parents d968f7c + ccc78d7 commit f1e5842

File tree

7 files changed

+85
-26
lines changed

7 files changed

+85
-26
lines changed

packages/pluggableWidgets/barcode-scanner-web/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66

77
## [Unreleased]
88

9+
### Fixed
10+
11+
- We fixed an issue with 1D barcode scanning, now 1D barcodes recognition is improved when using the mask (#180937)
12+
913
## [2.2.3] - 2023-01-04
1014

1115
### Changed

packages/pluggableWidgets/barcode-scanner-web/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "barcode-scanner-web",
33
"widgetName": "BarcodeScanner",
4-
"version": "2.2.3",
4+
"version": "2.2.4",
55
"description": "Displays a barcode scanner",
66
"copyright": "© Mendix Technology BV 2023. All rights reserved.",
77
"license": "Apache-2.0",

packages/pluggableWidgets/barcode-scanner-web/src/BarcodeScanner.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export const BarcodeScanner: FunctionComponent<BarcodeScannerContainerProps> = p
1919
);
2020
return (
2121
<BarcodeScannerComponent
22-
onDetect={props.onDetect ? onDetect : undefined}
22+
onDetect={onDetect}
2323
showMask={props.showMask}
2424
class={props.class}
2525
heightUnit={props.heightUnit}

packages/pluggableWidgets/barcode-scanner-web/src/components/BarcodeScanner.tsx

+5-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,11 @@ export function BarcodeScanner({
5757
...dimensions
5858
}: BarcodeScannerProps): ReactElement | null {
5959
const [errorMessage, setError] = useCustomErrorMessage();
60-
const videoRef = useReader({ onSuccess: onDetect, onError: setError });
60+
const videoRef = useReader({
61+
onSuccess: onDetect,
62+
onError: setError,
63+
useCrop: showMask
64+
});
6165
const supportsCameraAccess = typeof navigator?.mediaDevices?.getUserMedia === "function";
6266
const onCanPlay = useCallback((event: SyntheticEvent<HTMLVideoElement>) => {
6367
if (event.currentTarget.paused) {

packages/pluggableWidgets/barcode-scanner-web/src/hooks/useReader.ts

+68-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
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";
312
import { useEventCallback } from "@mendix/pluggable-widgets-commons";
413

514
const hints = new Map();
@@ -23,19 +32,61 @@ const mediaStreamConstraints: MediaStreamConstraints = {
2332
type UseReaderHook = (args: {
2433
onSuccess?: (data: string) => void;
2534
onError?: (e: Error) => void;
35+
useCrop: boolean;
2636
}) => RefObject<HTMLVideoElement>;
2737

2838
export const useReader: UseReaderHook = args => {
2939
const videoRef = useRef<HTMLVideoElement>(null);
3040
const onSuccess = useEventCallback(args.onSuccess);
3141
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+
};
3284

3385
useEffect(() => {
34-
let stopped = false;
35-
const reader = new BrowserMultiFormatReader(hints, 2000);
86+
const reader = new BrowserMultiFormatReader(hints, 500);
3687

3788
const stop = (): void => {
38-
stopped = true;
89+
stopped.current = true;
3990
reader.stopAsyncDecode();
4091
reader.reset();
4192
};
@@ -44,16 +95,24 @@ export const useReader: UseReaderHook = args => {
4495
let stream;
4596
try {
4697
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) {
50110
onSuccess(result.getText());
51111
}
52112
}
53113
} catch (error) {
54114
// 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)) {
57116
if (error instanceof Error) {
58117
console.error(error.message);
59118
}

packages/pluggableWidgets/barcode-scanner-web/src/package.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="utf-8" ?>
22
<package xmlns="http://www.mendix.com/package/1.0/">
3-
<clientModule name="Barcode Scanner" version="2.2.3" xmlns="http://www.mendix.com/clientModule/1.0/">
3+
<clientModule name="Barcode Scanner" version="2.2.4" xmlns="http://www.mendix.com/clientModule/1.0/">
44
<widgetFiles>
55
<widgetFile path="BarcodeScanner.xml" />
66
</widgetFiles>

packages/pluggableWidgets/barcode-scanner-web/src/ui/BarcodeScanner.scss

+5-13
Original file line numberDiff line numberDiff line change
@@ -33,24 +33,16 @@
3333
}
3434

3535
.canvas-left {
36-
flex: 1;
36+
flex: 0;
37+
flex-basis: 30%;
3738
}
3839

3940
$screen-md: 768px;
4041
$screen-lg: 992px;
4142
$screen-xl: 1200px;
4243
.canvas-middle {
4344
flex: 0;
44-
flex-basis: 60%;
45-
@media screen and (min-width: $screen-md) {
46-
flex-basis: 50%;
47-
}
48-
@media screen and (min-width: $screen-lg) {
49-
flex-basis: 40%;
50-
}
51-
@media screen and (min-width: $screen-xl) {
52-
flex-basis: 30%;
53-
}
45+
flex-basis: 40%;
5446
display: flex;
5547
flex-direction: column;
5648

@@ -62,7 +54,6 @@
6254
flex: 0;
6355
padding-top: 100%;
6456
position: relative;
65-
6657
$corner-offset: 13px;
6758
.corner {
6859
position: absolute;
@@ -101,7 +92,8 @@
10192
}
10293

10394
.canvas-right {
104-
flex: 1;
95+
flex: 0;
96+
flex-basis: 30%;
10597
}
10698
}
10799
}

0 commit comments

Comments
 (0)