How to Build a Simple PWA Barcode Reader
Progressive web apps (PWA), simply to say, are web apps that running like native apps on the desktop and mobile platforms. The advantages of PWA include installable, discoverable, linkable and so on. In this article, I’m trying to give a snapshot of a simple PWA barcode reader project built with Dynamsoft JavaScript barcode SDK.
What You Should Know About Dynamsoft JavaScript Barcode SDK
Why PWA
Since from the day that mobile became the mainstream, users are spending more of time in native apps rather than on the web.
However, there are some drawbacks of using native apps:
- It takes time to install native apps from the app store.
- Users tend to install many apps but only use a few of them, which is a waste to system space.
Progressive web apps feature native app capabilities, such as push notification, offline work, background sync, sensor access and so on.
How doe a Progressive Web App Work
The main component of PWA is a service worker that runs in the background for caching web resources and providing native capabilities.
PWA Browser Compatibility
Browser compatibility is important for web app development. You can find the PWA features supported by Chrome, Safari, Edge, Firefox and Opera here: https://www.goodbarber.com/blog/progressive-web-apps-feature-compatibility-based-on-the-browser-a883/.
Building and Publishing PWA Barcode Reader
A standard progressive web app has to meet the following criteria:
- Hosted over HTTPS.
- Include a web manifest file.
- Register a service worker.
We create a manifest file manifest.json as follows:
{
"name": "BarcodeReader",
"short_name": "BarcodeReader",
"description": "A progressive web app for reading barcodes.",
"icons": [
{
"src": "icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
}
],
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#2F3BA2",
"background_color": "#3E4EB8"
}
With the manifest file, your app can be installed to the Android home screen and be launched as a native app.
Then we create a simple service-worker.js file:
self.addEventListener('install', function(e) {
console.log('[ServiceWorker] Install');
});
self.addEventListener('fetch', function(e) {
console.log('[ServiceWorker] Fetch', e.request.url);
});
The service worker needs to be registered in the main thread:
if ('serviceWorker' in navigator) {
navigator.serviceWorker
.register('./service-worker.js')
.then(function () {
console.log('Service Worker Registered');
});
}
So far, the main PWA feature has been done. The next step is to add the barcode recognition SDK to the application. To get started quickly, we use the sample code in the GitHub repository of Dynamsoft JavaScript Barcode SDK.
Here are the implementation steps:
-
Initialize the barcode scanner object:
window.onload = async function () { try { await Dynamsoft.DBR.BarcodeScanner.loadWasm(); await initBarcodeScanner(); } catch (ex) { alert(ex.message); throw ex; } }; let scanner = null; async function initBarcodeScanner() { scanner = await Dynamsoft.DBR.BarcodeScanner.createInstance(); await scanner.show(); }
-
Update the runtime settings:
let settings = await scanner.getRuntimeSettings(); settings.deblurLevel = 0; settings.expectedBarcodesCount = 1; await scanner.updateRuntimeSettings(settings);
-
Register the callback function for barcode detection results:
scanner.onFrameRead = results => { console.log(results); for (let result of results) { document.getElementById('result').innerHTML = result.barcodeFormatString + ", " + result.barcodeText; } }; scanner.onUnduplicatedRead = (txt, result) => { };
-
Customize the UI for displaying the barcode scanning results:
scanner._drawRegionsults = function (e) { let t, i, n; if (this.beingLazyDrawRegionsults = !1, this.singleFrameMode) { if (!this.oriCanvas) return; t = "contain", i = this.oriCanvas.width, n = this.oriCanvas.height } else { if (!this._video) return; t = this._video.style.objectFit || "contain", i = this._video.videoWidth, n = this._video.videoHeight } let r = this.region; if (r && (!r.regionLeft && !r.regionRight && !r.regionTop && !r.regionBottom && !r.regionMeasuredByPercentage || r instanceof Array ? r = null : r.regionMeasuredByPercentage ? r = r.regionLeft || r.regionRight || 100 !== r.regionTop || 100 !== r.regionBottom ? { regionLeft: Math.round(r.regionLeft / 100 * i), regionTop: Math.round(r.regionTop / 100 * n), regionRight: Math.round(r.regionRight / 100 * i), regionBottom: Math.round(r.regionBottom / 100 * n) } : null : (r = JSON.parse(JSON.stringify(r)), delete r.regionMeasuredByPercentage)), this._cvsDrawArea) { this._cvsDrawArea.style.objectFit = t; let o = this._cvsDrawArea; o.width = i, o.height = n; let s = o.getContext("2d"); if (r) { s.fillStyle = this.regionMaskFillStyle, s.fillRect(0, 0, o.width, o.height), s.globalCompositeOperation = "destination-out", s.fillStyle = "#000"; let e = Math.round(this.regionMaskLineWidth / 2); s.fillRect(r.regionLeft - e, r.regionTop - e, r.regionRight - r.regionLeft + 2 * e, r.regionBottom - r.regionTop + 2 * e), s.globalCompositeOperation = "source-over", s.strokeStyle = this.regionMaskStrokeStyle, s.lineWidth = this.regionMaskLineWidth, s.rect(r.regionLeft, r.regionTop, r.regionRight - r.regionLeft, r.regionBottom - r.regionTop), s.stroke() } if (e) { s.globalCompositeOperation = "destination-over", s.fillStyle = this.barcodeFillStyle, s.strokeStyle = this.barcodeStrokeStyle, s.lineWidth = this.barcodeLineWidth, e = e || []; for (let t of e) { let e = t.localizationResult; s.beginPath(), s.moveTo(e.x1, e.y1), s.lineTo(e.x2, e.y2), s.lineTo(e.x3, e.y3), s.lineTo(e.x4, e.y4), s.fill(), s.beginPath(), s.moveTo(e.x1, e.y1), s.lineTo(e.x2, e.y2), s.lineTo(e.x3, e.y3), s.lineTo(e.x4, e.y4), s.closePath(), s.stroke() let text = t.barcodeText; s.font = '18px Verdana'; s.fillStyle = '#ff0000'; let x = [e.x1, e.x2, e.x3, e.x4]; let y = [e.y1, e.y2, e.y3, e.y4]; x.sort(function (a, b) { return a - b; }); y.sort(function (a, b) { return b - a; }); let left = x[0]; let top = y[0]; s.fillText(text, left, top + 50); } } this.singleFrameMode && (s.globalCompositeOperation = "destination-over", s.drawImage(this.oriCanvas, 0, 0)) } if (this._divScanArea) { let e = this._video.offsetWidth , t = this._video.offsetHeight , o = 1; e / t < i / n ? (o = e / i, this._divScanArea.style.left = "0", this._divScanArea.style.top = Math.round((t - n * o) / 2) + "px") : (o = t / n, this._divScanArea.style.left = Math.round((e - i * o) / 2) + "px", this._divScanArea.style.top = "0"); let s = r ? Math.round(r.regionLeft * o) : 0 , a = r ? Math.round(r.regionTop * o) : 0 , d = r ? Math.round(r.regionRight * o - s) : Math.round(i * o) , _ = r ? Math.round(r.regionBottom * o - a) : Math.round(n * o); this._divScanArea.style.marginLeft = s + "px", this._divScanArea.style.marginTop = a + "px", this._divScanArea.style.width = d + "px", this._divScanArea.style.height = _ + "px" } } document.getElementById('barcodeScanner').appendChild(scanner.getUIElement()); document.getElementById('loading-status').hidden = true; document.getElementsByClassName('dbrScanner-sel-camera')[0].hidden = true; document.getElementsByClassName('dbrScanner-sel-resolution')[0].hidden = true; document.getElementsByClassName('dbrScanner-btn-close')[0].hidden = true;
You can now test the PWA project with a web server. GitHub page is a nice choice for deploying and testing your PWA project.
Try my PWA barcode reader.
Desktop
Mobile
Reference
- https://developers.google.com/web/fundamentals/codelabs/your-first-pwapp/
- https://developers.google.com/web/fundamentals/primers/service-workers/
- https://youtu.be/z2JgN6Ae-Bo
- https://github.com/mdn/pwa-examples
Source Code
https://github.com/yushulx/javascript-barcode/tree/master/examples/web/pwa
About Dynamsoft Barcode Reader JavaScript Edition
Dynamsoft Barcode Reader JavaScript Edition is a JavaScript barcode scanning library based on the WebAssembly technology. It supports Code 39, Code 93, Code 128, Codabar, EAN-8, EAN-13, UPC-A, UPC-E, Interleaved 2 of 5 (ITF), Industrial 2 of 5 (Code 2 of 5 Industry, Standard 2 of 5, Code 2 of 5), ITF-14 QR code, Datamatrix, PDF417 and Aztec code. The library is capable of scanning multiple barcodes from static images and camera video stream.