Skip to content

Commit 7dcd6c8

Browse files
authored
fix: native BarocdeDetector doesn't support requested formats (#451)
fix: native `BarocdeDetector` doesn't support requested formats Previously we always used the native `BarocdeDetector` implementation if available. However, the native API might be available but does not support the barcode formats requested by the user. For example, `"qr_code"` might be supported but the user wants to scan `["qr_code", "aztec"]`. In these cases, we now also fallback to the polyfill implementation. Closes #450
1 parent aa7c5aa commit 7dcd6c8

File tree

3 files changed

+58
-20
lines changed

3 files changed

+58
-20
lines changed

docs/api/QrcodeStream.md

+19-1
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,30 @@ However changing the value of `paused` resets this internal cache.
9898
- **Default:** `['qr_code']`
9999

100100
The `formats` prop defines which barcode formats are detected.
101-
[Supported Formats](https://github.com/Sec-ant/barcode-detector?tab=readme-ov-file#barcode-detector).
101+
By default, only QR codes are selected,
102+
so if you want to scan other barcode formats,
103+
you have to modify this prop.
104+
See: [supported formats](https://github.com/Sec-ant/barcode-detector?tab=readme-ov-file#barcode-detector).
102105

103106
```html
104107
<qrcode-stream :formats="['qr_code', 'code_128']"></qrcode-stream>
105108
```
106109

110+
::: warning
111+
Don't select more barcode formats than needed.
112+
Scanning becomes more expensive the more formats you select.
113+
:::
114+
115+
Under the hood, we use the standard
116+
[`BarcodeDetector`](https://developer.mozilla.org/en-US/docs/Web/API/BarcodeDetector)
117+
browser API.
118+
Support varies across devices, operating systems and browsers.
119+
The component will prefer to use the native implementation if available and otherwise falls back to a polyfill implementation.
120+
Note that even if the native implementation is availabe,
121+
the component still might use the polyfill.
122+
For example, if the native implementation only supports the
123+
format `'qr_code'` but the you select the formats `['qr_code', 'aztec']`.
124+
107125
### `camera-on` <Badge text="since v5.0.0" type="info" />
108126

109127
- **Payload Type:** `Promise<MediaTrackCapabilities>`

src/components/QrcodeStream.vue

+3-3
Original file line numberDiff line numberDiff line change
@@ -231,10 +231,10 @@ watch(
231231
{ deep: true }
232232
)
233233
234-
// Set formats will create a new BarcodeDetector instance with the given formats.
235-
watch(formatsCached, (formats) => {
234+
// `setScanningFormats` will create a new BarcodeDetector instance with the given formats.
235+
watch(formatsCached, async formats => {
236236
if (isMounted.value) {
237-
setScanningFormats(formats)
237+
await setScanningFormats(formats)
238238
}
239239
})
240240

src/misc/scanner.ts

+36-16
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,44 @@ declare global {
2121
let barcodeDetector: BarcodeDetector
2222

2323
/**
24-
* Seamlessly updates the set of used barcode formats during scanning.
24+
* Constructs a `BarcodeDetector` instance, given a list of targeted barcode formats.
25+
* Preferably, we want to use the native `BarcodeDetector` implementation if supported.
26+
* Otherwise, we fall back to the polyfill implementation.
27+
*
28+
* Note, that we can't just monkey patch the polyfill on load, i.e.
29+
*
30+
* window.BarcodeDetector ??= BarcodeDetector
31+
*
32+
* for two reasons. Firstly, this is not SSR compatible, because `window` is not available
33+
* during SSR. Secondly, even if the native implementation is availabe, we still might
34+
* want to use the polyfill. For example, if the native implementation only supports the
35+
* format `"qr_code"` but the user wants to scan `["qr_code", "aztec"]` (see #450).
2536
*/
26-
export function setScanningFormats(formats: BarcodeFormat[]) {
27-
// Only use the `BarcodeDetector` polyfill if the API is not supported natively.
28-
//
29-
// Note, that we can't just monkey patch the API on load, i.e.
30-
//
31-
// globalThis.BarcodeDetector ??= BarcodeDetector
32-
//
33-
// because that is not SSR compatible. If the polyfill is applied during SSR, then
34-
// it's actually missing at runtime. Thus, we have to check the API support at runtime:
37+
async function createBarcodeDetector(formats: BarcodeFormat[]): Promise<BarcodeDetector> {
3538
if (window.BarcodeDetector === undefined) {
36-
console.debug('[vue-qrcode-reader] BarcodeDetector not available: will use polyfill.')
37-
barcodeDetector = new BarcodeDetector({ formats })
38-
} else {
39-
console.debug('[vue-qrcode-reader] BarcodeDetector available: will use native API.')
40-
barcodeDetector = new window.BarcodeDetector({ formats })
39+
console.debug('[vue-qrcode-reader] Native BarcodeDetector not supported. Will use polyfill.')
40+
return new BarcodeDetector({ formats })
41+
}
42+
43+
const allSupportedFormats = await window.BarcodeDetector.getSupportedFormats()
44+
const unsupportedFormats = formats.filter(format => !allSupportedFormats.includes(format))
45+
46+
if (unsupportedFormats.length > 0) {
47+
console.debug(`[vue-qrcode-reader] Native BarcodeDetector does not support formats ${JSON.stringify(unsupportedFormats)}. Will use polyfill.`)
48+
return new BarcodeDetector({ formats })
4149
}
50+
51+
console.debug('[vue-qrcode-reader] Will use native BarcodeDetector.')
52+
return new window.BarcodeDetector({ formats })
53+
}
54+
55+
/**
56+
* Update the set of targeted barcode formats. In particular, this function
57+
* can be called during scanning and the camera stream doesn't have to be
58+
* interrupted.
59+
*/
60+
export async function setScanningFormats(formats: BarcodeFormat[]) {
61+
barcodeDetector = await createBarcodeDetector(formats)
4262
}
4363

4464
type ScanHandler = (_: DetectedBarcode[]) => void
@@ -62,7 +82,7 @@ export const keepScanning = async (
6282
}
6383
) => {
6484
console.debug('[vue-qrcode-reader] start scanning')
65-
setScanningFormats(formats)
85+
await setScanningFormats(formats)
6686

6787
const processFrame =
6888
(state: { lastScanned: number; contentBefore: string[]; lastScanHadContent: boolean }) =>

0 commit comments

Comments
 (0)