Skip to content

Instantly share code, notes, and snippets.

@hermantolim
Forked from DeadlySystem/QR Code Scanner
Created March 22, 2022 12:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hermantolim/3ef6239bcf2af23cc49b2085e73847e7 to your computer and use it in GitHub Desktop.
Save hermantolim/3ef6239bcf2af23cc49b2085e73847e7 to your computer and use it in GitHub Desktop.
This is code that provides an interface to let you select the camera to be used with jsqrcode ( https://github.com/LazarSoft/jsqrcode ). To make it work, you will have to include said library and have elements with the IDs in the loadCommonHTMLElements method on your page. You may want to provide appropriate styling as well. This source code is …
"use strict";
define(["require", "exports"], function (require, exports) {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Browser compatibility definitions
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Private state
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
var qrcodescanner = null;
var cameralist = null;
var camerafeed = null;
var camerastream = null;
var canvas = null;
var drawingcontext = null;
var readtimeout = null;
var scanfrequency = 333; //In milliseconds
var nocameras = false;
var ready = false;
var closed = true;
var successcallback = defaultSuccess;
var failurecallback = defaultFailure;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Exports
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function load() {
if (ready) {
//Don't initialize twice
return;
}
loadCommonHTMLElements();
installEventListeners();
//Find all cameras and add them to the dropdown
MediaStreamTrack.getSources(function (sources) {
//Find video sources
var videosources = sources.filter(function (source) {
return source.kind === "video";
});
//Check if there are any video sources
if (videosources.length === 0) {
//If there are no video sources at all, the scanner will not work.
nocameras = true;
return;
}
//Add all video sources to the dropdown for camera selection
var unlabelledcameraindex = 1;
videosources.forEach(function (videosource) {
var option = document.createElement("option");
option.value = videosource.id; //TODO: Apparently Chrome-specific; W3C specifies sourceId.
//Establish the label for the current video source
var cameralabel = videosource.label;
if (cameralabel === "") {
cameralabel = "Camera " + unlabelledcameraindex;
++unlabelledcameraindex;
}
if (videosource.facing) {
//The facing-property is Chrome-specific
cameralabel += " - " + videosource.facing;
if (videosource.facing === "environment") {
//Pre-select the video source facing the environment
option.selected = true;
}
}
option.text = cameralabel;
//Add the video source to the dropdown
cameralist.appendChild(option);
});
//Signal that the QR code scanner is now ready
ready = true;
});
}
exports.load = load;
function scan(onsuccess, onfailure) {
if (nocameras) {
//If there are no cameras, the scanner cannot operate - fail right away
failure("This device does not seem to have any cameras.");
return;
}
if (!ready) {
//If the scanner is not ready yet, try again later
window.setTimeout(scan, 200);
return;
}
//Default values for parameters
if (onsuccess === undefined || typeof onsuccess !== "function") {
onsuccess = defaultSuccess;
}
if (onfailure === undefined || typeof onfailure !== "function") {
onfailure = defaultFailure;
}
//Set up callbacks
successcallback = onsuccess;
failurecallback = onfailure;
//Set state to opened
closed = false;
//Clear the canvas (avoids re-scanning the previous QR code)
drawingcontext.clearRect(0, 0, canvas.width, canvas.height);
//Request camera access
requestCameraAccess(cameralist.value);
//Show the scanner
qrcodescanner.style.display = "block";
}
exports.scan = scan;
function close() {
closed = true;
window.clearTimeout(readtimeout);
if (camerastream !== null) {
camerastream.stop();
}
qrcodescanner.style.display = "none";
}
exports.close = close;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Private helper methods
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function loadCommonHTMLElements() {
/* The types are as follows:
qrcodescanner = <HTMLDivElement> document.getElementById("QRCodeScannerOverlay");
cameralist = <HTMLSelectElement> document.getElementById("cameraselection");
camerafeed = <HTMLVideoElement> document.getElementById("camerafeed");
canvas = <HTMLCanvasElement> document.getElementById("qr-canvas");
*/
qrcodescanner = document.getElementById("QRCodeScannerOverlay");
cameralist = document.getElementById("cameraselection");
camerafeed = document.getElementById("camerafeed");
canvas = document.getElementById("qr-canvas");
//Not really an HTML element, but anyway
drawingcontext = canvas.getContext("2d");
}
function installEventListeners() {
//Add handler for camera selection
cameralist.addEventListener("change", function () {
requestCameraAccess(cameralist.value);
});
//Set up callback for QR code decoding success
qrcode.callback = success;
}
function requestCameraAccess(sourceId) {
if (camerastream !== null) {
//The following is required to enable switching between cameras, as there cannot be multiple concurrent streams
camerastream.stop();
}
//Request access to the specified camera
navigator.getUserMedia({
"video": {
"optional": [
{ "sourceId": sourceId }
]
},
"audio": false
}, function (stream) {
camerastream = stream;
camerafeed.src = URL.createObjectURL(stream);
tryQRCodeReading();
}, function (error) {
window["e"] = error;
failure("Could not access the camera (" + error.name + "). " + error.message);
});
}
function tryQRCodeReading() {
try {
drawingcontext.drawImage(camerafeed, 0, 0);
try {
qrcode.decode();
}
catch (e) {
//Happens all the time if QR code scanning did not succeed
//console.log("QR code decoding status: " + e);
if (!closed) {
readtimeout = window.setTimeout(tryQRCodeReading, scanfrequency);
}
}
}
catch (e) {
failure(e);
}
}
function success(data) {
navigator.vibrate([500]);
close();
successcallback(data);
}
function failure(errormessage) {
close();
failurecallback(errormessage);
}
function defaultSuccess(data) {
alert(data);
}
function defaultFailure(errormessage) {
alert("Error: " + errormessage);
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment