chore: switched to zxing for barcode scanning if native not available
af336d9f
1 file(s) · +55 −20
| 87 | 87 | {% endif %} |
|
| 88 | 88 | </section> |
|
| 89 | 89 | ||
| 90 | + | <script src="https://unpkg.com/@zxing/browser@0.1.5/umd/zxing-browser.min.js"></script> |
|
| 90 | 91 | <script> |
|
| 91 | 92 | async function searchBooks() { |
|
| 92 | 93 | const q = document.getElementById('book-query').value.trim(); |
|
| 193 | 194 | ||
| 194 | 195 | let scanStream = null; |
|
| 195 | 196 | let scanRaf = null; |
|
| 197 | + | let zxingControls = null; |
|
| 198 | + | ||
| 199 | + | const hasNativeBarcode = 'BarcodeDetector' in window; |
|
| 200 | + | const hasZxing = typeof ZXingBrowser !== 'undefined'; |
|
| 196 | 201 | ||
| 197 | 202 | (function initScan() { |
|
| 198 | - | if ('BarcodeDetector' in window && navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { |
|
| 203 | + | if ((hasNativeBarcode || hasZxing) && navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { |
|
| 199 | 204 | document.getElementById('scan-btn').hidden = false; |
|
| 200 | 205 | } |
|
| 201 | 206 | })(); |
|
| 206 | 211 | const status = document.getElementById('scan-status'); |
|
| 207 | 212 | status.textContent = 'Point camera at barcode'; |
|
| 208 | 213 | modal.hidden = false; |
|
| 214 | + | ||
| 215 | + | const onHit = (isbn) => { |
|
| 216 | + | closeScanner(); |
|
| 217 | + | document.getElementById('book-query').value = isbn; |
|
| 218 | + | searchBooks(); |
|
| 219 | + | }; |
|
| 220 | + | ||
| 209 | 221 | try { |
|
| 210 | - | scanStream = await navigator.mediaDevices.getUserMedia({ |
|
| 211 | - | video: { facingMode: 'environment' } |
|
| 212 | - | }); |
|
| 213 | - | video.srcObject = scanStream; |
|
| 214 | - | await video.play(); |
|
| 215 | - | const detector = new BarcodeDetector({ formats: ['ean_13', 'ean_8', 'upc_a'] }); |
|
| 216 | - | const tick = async () => { |
|
| 217 | - | if (!scanStream) return; |
|
| 222 | + | let detector = null; |
|
| 223 | + | if (hasNativeBarcode) { |
|
| 218 | 224 | try { |
|
| 219 | - | const codes = await detector.detect(video); |
|
| 220 | - | if (codes.length) { |
|
| 221 | - | const isbn = codes[0].rawValue; |
|
| 222 | - | closeScanner(); |
|
| 223 | - | document.getElementById('book-query').value = isbn; |
|
| 224 | - | searchBooks(); |
|
| 225 | - | return; |
|
| 225 | + | detector = new BarcodeDetector({ formats: ['ean_13', 'ean_8', 'upc_a'] }); |
|
| 226 | + | } catch (_) { |
|
| 227 | + | detector = null; |
|
| 228 | + | } |
|
| 229 | + | } |
|
| 230 | + | ||
| 231 | + | if (detector) { |
|
| 232 | + | scanStream = await navigator.mediaDevices.getUserMedia({ |
|
| 233 | + | video: { facingMode: 'environment' } |
|
| 234 | + | }); |
|
| 235 | + | video.srcObject = scanStream; |
|
| 236 | + | await video.play(); |
|
| 237 | + | const tick = async () => { |
|
| 238 | + | if (!scanStream) return; |
|
| 239 | + | try { |
|
| 240 | + | const codes = await detector.detect(video); |
|
| 241 | + | if (codes.length) return onHit(codes[0].rawValue); |
|
| 242 | + | } catch (_) {} |
|
| 243 | + | scanRaf = requestAnimationFrame(tick); |
|
| 244 | + | }; |
|
| 245 | + | tick(); |
|
| 246 | + | return; |
|
| 247 | + | } |
|
| 248 | + | ||
| 249 | + | if (hasZxing) { |
|
| 250 | + | const reader = new ZXingBrowser.BrowserMultiFormatReader(); |
|
| 251 | + | zxingControls = await reader.decodeFromVideoDevice(undefined, video, (result, err, controls) => { |
|
| 252 | + | if (result) { |
|
| 253 | + | controls.stop(); |
|
| 254 | + | zxingControls = null; |
|
| 255 | + | onHit(result.getText()); |
|
| 226 | 256 | } |
|
| 227 | - | } catch (_) {} |
|
| 228 | - | scanRaf = requestAnimationFrame(tick); |
|
| 229 | - | }; |
|
| 230 | - | tick(); |
|
| 257 | + | }); |
|
| 258 | + | return; |
|
| 259 | + | } |
|
| 260 | + | ||
| 261 | + | status.textContent = 'Scanner not supported'; |
|
| 231 | 262 | } catch (e) { |
|
| 232 | 263 | status.textContent = 'Camera unavailable'; |
|
| 233 | 264 | } |
|
| 236 | 267 | function closeScanner() { |
|
| 237 | 268 | if (scanRaf) cancelAnimationFrame(scanRaf); |
|
| 238 | 269 | scanRaf = null; |
|
| 270 | + | if (zxingControls) { |
|
| 271 | + | try { zxingControls.stop(); } catch (_) {} |
|
| 272 | + | zxingControls = null; |
|
| 273 | + | } |
|
| 239 | 274 | if (scanStream) { |
|
| 240 | 275 | scanStream.getTracks().forEach(function(t) { t.stop(); }); |
|
| 241 | 276 | scanStream = null; |
|