Introduction
QuaggaJS is a javascript library project maintained by Christoph Oberhofer, that allows devices to use their cameras as barcode scanners, both in static and live images. Regardless of the plethora of features this library offers, in this article I’m going to focus on the live streaming of the device camera to read the barcode image.
Tech Stats
Browser Support (live stream)
Readable Barcode Types
- EAN
- CODE 128
- CODE 39
- EAN 8
- UPC-A
- UPC-C
- I2of5
- 2of5
- CODE 93
- CODABAR
- Getting Quagga
First and foremost, it was difficult at first to find information on QuaggaJS. The majority of other developers I worked with despised web barcode readers citing that they were slow at reading and difficult to work with. I found Quagga to be very easy to work with, after some minor modifications. If anyone, including the creator would like to correct me, please do.
At first I downloaded and installed the library from the website:
https://serratus.github.io/quaggaJS
The I realized their might be a simpler way through use of a CDN where both the full and minified versions of the QuaggaJS library are available:
https://cdnjs.com/libraries/quagga
Once included, I also used CDNs for the jquery libraries in my project layout page.
Using QuaggaJS
In my project I’m using MVC.NET 4.5 where my controller loads my view and no model. The view has a corresponding js file containing all of my javascript for the page. I’ll focus on this file for this article.
I decided that I wanted this QuaggaJS project to behave in the following manner:
- You can scan as many barcodes as you like after pressing the “Start” button.
- The button text will toggle between “Start” and “Stop”.
- Information about the last scanned barcode is placed in a hidden field.
- Stopping the scanner, triggers an AJAX call to get information about the barcode scanned.
In the code below you will see that I have jQuery listening for the button with ID “btnScan” to be clicked. This section controls everything on the page, there is no JS in my markup page.
var _scannerIsRunning = false;
$(document).ready(function () {
$("#btnScan").click(function () {
if (_scannerIsRunning) {
_scannerIsRunning = false;
Quagga.stop();
$("#btnScan").text("Start");
$("#scanner-container").hide();
} else {
startScanner();
$("video").attr("width", "100%");
$("#btnScan").text("Stop");
$("#scanner-container").show();
}
});
LoadPage();
});
The majority of the heavy lifting is done by a single function. I’ve configured my function for my specific needs and commented out additional options that I did not make use of. You can see that the first part below initializes the Quagga object including “type: ‘LiveStream’” instead of Static Images, and various constraints such as aspect ratio and facingmode, in reference to the camera facing.
function startScanner() {
Quagga.init({
inputStream: {
name: "Live",
type: "LiveStream",
target: document.querySelector('#scanner-container'),
constraints: {
facingMode: "environment",
"aspectRatio": { "min": 1, "max": 100 }
},
},
"locator": { "patchSize": "medium", "halfSample": false },
"numOfWorkers": 1,
The second part of the initializer includes scanning frequency, used for the number of times in a timeframe it will attempt to scan, and numOfWorkers- which from what I could surmise, was the number of threads attempting to locate and read a barcode in the live stream. I reduced mine from 10 to 1 and it seemed to work a little faster on in my browser (Google Chrome Version 60.0.3112.113 (Official Build) (64-bit)). The initializer also includes the decoder. Below is the decoder I used and commented out are other available decoders and the “debug” option.
"locator": { "patchSize": "medium", "halfSample": false },
"numOfWorkers": 1,
"frequency": 10,
"decoder": { "readers": [{ "format": "code_39_reader", "config": {} }] },
"locate": true,
There are two more sections inside the startScanner function which handle the “onProcessed” and “onDetected” events for the QuaggaJS object. The examples I found online for “onProcessed were flexible enough that I didn’t need to alter them, however, I did make some changes to the “onDetected” handler for the specifics of my application.
As you can see below, my onDetected handler loads the hidden field with the scanned code value, loads information to the page using a function that calls ajax, then automatically clicks the button, if I wished that found barcode would close the viewscreen of the scanner and toggle my button text. Removing it would bring the application closer to the version I mentioned in my opening statements.
Quagga.onDetected(function (result) {
console.log("Barcode detected and processed : [" + result.codeResult.code + "]", result);
$("#hdnBarcode").val(result.codeResult.code);
LoadPage();
$("#btnScan").click();
});
As you can see QuaggaJS is pretty simple to use for this one purpose, while having a multitude of functional operations such as scanning static images and more. I hope this helps someone out there and please feel free to comment on this if you have QuaggaJS experience.
In Closing
I didn't find any other articles on QuaggaJS in CodeProject, so I hope this article helps someone with their own implementation. Feel free to comment and contribute as I am not a QuaggaJS guru. Below is the full js file.
var _scannerIsRunning = false;
$(document).ready(function () {
$("#btnScan").click(function () {
if (_scannerIsRunning) {
_scannerIsRunning = false;
Quagga.stop();
$("#btnScan").text("Start");
$("#scanner-container").hide();
} else {
startScanner();
$("video").attr("width", "100%");
$("#btnScan").text("Stop");
$("#scanner-container").show();
}
});
LoadPage();
});
function closeModal() {
$('.modal').hide();
}
function startScanner() {
Quagga.init({
inputStream: {
name: "Live",
type: "LiveStream",
target: document.querySelector('#scanner-container'),
constraints: {
facingMode: "environment",
"aspectRatio": { "min": 1, "max": 100 }
},
},
"locator": { "patchSize": "medium", "halfSample": false },
"numOfWorkers": 1,
"frequency": 10,
"decoder": { "readers": [{ "format": "code_39_reader", "config": {} }] },
"locate": true
}, function (err) {
if (err) {
console.log(err);
return
}
console.log("Initialization finished. Ready to start");
Quagga.start();
_scannerIsRunning = true;
});
Quagga.onProcessed(function (result) {
var drawingCtx = Quagga.canvas.ctx.overlay,
drawingCanvas = Quagga.canvas.dom.overlay;
if (result) {
if (result.boxes) {
drawingCtx.clearRect(0, 0, parseInt(drawingCanvas.getAttribute("width")), parseInt(drawingCanvas.getAttribute("height")));
result.boxes.filter(function (box) {
return box !== result.box;
}).forEach(function (box) {
Quagga.ImageDebug.drawPath(box, { x: 0, y: 1 }, drawingCtx, { color: "green", lineWidth: 2 });
});
}
if (result.box) {
Quagga.ImageDebug.drawPath(result.box, { x: 0, y: 1 }, drawingCtx, { color: "#00F", lineWidth: 2 });
}
if (result.codeResult && result.codeResult.code) {
Quagga.ImageDebug.drawPath(result.line, { x: 'x', y: 'y' }, drawingCtx, { color: 'red', lineWidth: 3 });
}
}
});
Quagga.onDetected(function (result) {
console.log("Barcode detected and processed : [" + result.codeResult.code + "]", result);
$("#hdnBarcode").val(result.codeResult.code);
LoadPage();
$("#btnScan").click();
});
}
function LoadPage()
{
if ($("#hdnBarcode").val() == "")
$("#hdnBarcode").val("1900067611");
$.ajax({
type: 'Get',
url: 'Home/GetData/' + $("#hdnBarcode").val(),
success: function (json) {
}
});
}