diff --git a/example/css/styles.css b/example/css/styles.css index 4c7d74b..8e2fedb 100644 --- a/example/css/styles.css +++ b/example/css/styles.css @@ -48,6 +48,7 @@ } .overlay { + display: none; overflow: hidden; position: fixed; top: 0; @@ -58,7 +59,7 @@ background-color: rgba(0, 0, 0, 0.3); } -.overlay__content { +.overlay__container { top: 50%; position: absolute; left: 50%; diff --git a/example/scan-to-input/index.html b/example/scan-to-input/index.html index f94d008..f19c911 100644 --- a/example/scan-to-input/index.html +++ b/example/scan-to-input/index.html @@ -52,22 +52,24 @@ var App = { _scanner: null, init: function() { this.attachListeners(); + this._overlay = document.querySelector('.overlay'); }, activateScanner: function() { - var scanner = this.configureScanner('.overlay__content'), - onDetected = function (result) { - document.querySelector('input.isbn').value = result.codeResult.code; - stop(); - }.bind(this), - stop = function() { - scanner.stop(); // should also clear all event-listeners? - scanner.removeEventListener('detected', onDetected); - this.hideOverlay(); - this.attachListeners(); - }.bind(this); - - this.showOverlay(stop); - scanner.addEventListener('detected', onDetected).start(); + this.configureScanner('.overlay__content') + .then(function(scanner) { + this.showOverlay(scanner.stop.bind(scanner)); + return scanner.detect(); + }.bind(this)) + .then(function(result) { + document.querySelector('input.isbn').value = result.codeResult.code; + }) + .catch((e) => { + console.log(e); + }) + .then(function() { + this.hideOverlay(); + this.attachListeners(); + }.bind(this)); }, attachListeners: function() { var self = this, @@ -80,29 +82,11 @@ var App = { }); }, showOverlay: function(cancelCb) { - if (!this._overlay) { - var content = document.createElement('div'), - closeButton = document.createElement('div'); - - closeButton.appendChild(document.createTextNode('X')); - content.className = 'overlay__content'; - closeButton.className = 'overlay__close'; - this._overlay = document.createElement('div'); - this._overlay.className = 'overlay'; - this._overlay.appendChild(content); - content.appendChild(closeButton); - closeButton.addEventListener('click', function closeClick() { - closeButton.removeEventListener('click', closeClick); - cancelCb(); - }); - document.body.appendChild(this._overlay); - } else { - var closeButton = document.querySelector('.overlay__close'); - closeButton.addEventListener('click', function closeClick() { - closeButton.removeEventListener('click', closeClick); - cancelCb(); - }); - } + var closeButton = document.querySelector('.overlay__close'); + closeButton.addEventListener('click', function closeClick() { + closeButton.removeEventListener('click', closeClick); + cancelCb(); + }); this._overlay.style.display = "block"; }, hideOverlay: function() { @@ -111,20 +95,18 @@ var App = { } }, configureScanner: function(selector) { - if (!this._scanner) { - this._scanner = Quagga - .decoder({readers: ['ean_reader']}) - .locator({patchSize: 'medium'}) - .fromVideo({ - target: selector, - constraints: { - width: 800, - height: 600, - facingMode: "environment" - } - }); + if (this._scanner) { + return Promise.resolve(this._scanner); } - return this._scanner; + + return Quagga.fromCamera({ + decoder: {readers: ['ean_reader']}, + target: selector, + }) + .then((scanner) => { + this._scanner = scanner; + return scanner; + }); } }; App.init(); @@ -141,6 +123,12 @@ App.init(); <button type="button" class="icon-barcode button scan"> </button> </div> </form> +<div class="overlay"> + <div class="overlay__container"> + <div class="overlay__close">X</div> + <div class="overlay__content" /> + </div> +</div> @@ -151,7 +139,12 @@ App.init(); © Copyright by Christoph Oberhofer
- + diff --git a/example/scan-to-input/index.js b/example/scan-to-input/index.js index 09d2d15..0ea7a28 100644 --- a/example/scan-to-input/index.js +++ b/example/scan-to-input/index.js @@ -3,22 +3,24 @@ var App = { _scanner: null, init: function() { this.attachListeners(); + this._overlay = document.querySelector('.overlay'); }, activateScanner: function() { - var scanner = this.configureScanner('.overlay__content'), - onDetected = function (result) { - document.querySelector('input.isbn').value = result.codeResult.code; - stop(); - }.bind(this), - stop = function() { - scanner.stop(); // should also clear all event-listeners? - scanner.removeEventListener('detected', onDetected); - this.hideOverlay(); - this.attachListeners(); - }.bind(this); - - this.showOverlay(stop); - scanner.addEventListener('detected', onDetected).start(); + this.configureScanner('.overlay__content') + .then(function(scanner) { + this.showOverlay(scanner.stop.bind(scanner)); + return scanner.detect(); + }.bind(this)) + .then(function(result) { + document.querySelector('input.isbn').value = result.codeResult.code; + }) + .catch((e) => { + console.log(e); + }) + .then(function() { + this.hideOverlay(); + this.attachListeners(); + }.bind(this)); }, attachListeners: function() { var self = this, @@ -31,29 +33,11 @@ var App = { }); }, showOverlay: function(cancelCb) { - if (!this._overlay) { - var content = document.createElement('div'), - closeButton = document.createElement('div'); - - closeButton.appendChild(document.createTextNode('X')); - content.className = 'overlay__content'; - closeButton.className = 'overlay__close'; - this._overlay = document.createElement('div'); - this._overlay.className = 'overlay'; - this._overlay.appendChild(content); - content.appendChild(closeButton); - closeButton.addEventListener('click', function closeClick() { - closeButton.removeEventListener('click', closeClick); - cancelCb(); - }); - document.body.appendChild(this._overlay); - } else { - var closeButton = document.querySelector('.overlay__close'); - closeButton.addEventListener('click', function closeClick() { - closeButton.removeEventListener('click', closeClick); - cancelCb(); - }); - } + var closeButton = document.querySelector('.overlay__close'); + closeButton.addEventListener('click', function closeClick() { + closeButton.removeEventListener('click', closeClick); + cancelCb(); + }); this._overlay.style.display = "block"; }, hideOverlay: function() { @@ -62,20 +46,18 @@ var App = { } }, configureScanner: function(selector) { - if (!this._scanner) { - this._scanner = Quagga - .decoder({readers: ['ean_reader']}) - .locator({patchSize: 'medium'}) - .fromSource({ - target: selector, - constraints: { - width: 800, - height: 600, - facingMode: "environment" - } - }); + if (this._scanner) { + return Promise.resolve(this._scanner); } - return this._scanner; + + return Quagga.fromCamera({ + decoder: {readers: ['ean_reader']}, + target: selector, + }) + .then((scanner) => { + this._scanner = scanner; + return scanner; + }); } }; App.init(); diff --git a/src/input/CameraSource.js b/src/input/CameraSource.js index 03e5969..33720a9 100644 --- a/src/input/CameraSource.js +++ b/src/input/CameraSource.js @@ -208,6 +208,12 @@ export function fromCamera(constraints, {target, scope = Scope.EXTERNAL} = {}) { stop() { track.stop(); }, + waitUntilReady() { + if (track.readyState === "live") { + return Promise.resolve(); + } + return this.applyConstraints(constraints); + }, getScope() { return scope; } diff --git a/src/input/SourceInterface.js b/src/input/SourceInterface.js index b7c5359..9f54e9c 100644 --- a/src/input/SourceInterface.js +++ b/src/input/SourceInterface.js @@ -9,6 +9,9 @@ class Source { getLabel() {} stop() {} getScope() {} + waitUntilReady() { + return Promise.resolve(); + } } export {Source}; @@ -23,5 +26,8 @@ export function generateSourceInterface() { getLabel() {}, stop() {}, getScope() {}, + waitUntilReady() { + return Promise.resolve(); + }, }; }; diff --git a/src/quagga.js b/src/quagga.js index 5d6b710..751405a 100644 --- a/src/quagga.js +++ b/src/quagga.js @@ -23,6 +23,7 @@ function fromConfig(pixelCapturer, config) { let currentConfig = config; let pendingStart = null; let initialized = false; + let cancelRequested = false; return { addEventListener(eventType, cb) { scanner.subscribe(eventType, cb); @@ -52,6 +53,7 @@ function fromConfig(pixelCapturer, config) { return pendingStart; }, stop() { + cancelRequested = true; scanner.stop(); initialized = false; return this; @@ -59,28 +61,27 @@ function fromConfig(pixelCapturer, config) { detect() { if (source.type === 'CAMERA' || source.type === 'VIDEO') { - let cancelRequested = false; - return { - cancel() { - cancelRequested = true; - }, - promise: new Promise((resolve, reject) => { + cancelRequested = false; + return this.start() + .then(() => { + return new Promise((resolve, reject) => { function onProcessed(result) { if (result && result.codeResult && result.codeResult.code) { scanner.stop(); scanner.unsubscribe("processed", onProcessed); + scanner.unsubscribe("stopped", onProcessed); resolve(result); } if (cancelRequested) { - scanner.stop(); scanner.unsubscribe("processed", onProcessed); + scanner.unsubscribe("stopped", onProcessed); reject("cancelled!"); } } scanner.subscribe("processed", onProcessed); - this.start(); - }) - }; + scanner.subscribe("stopped", onProcessed, true); + }); + }); } else { let pendingDecodeSingle = Promise.resolve(); if (!initialized) { diff --git a/src/scanner.js b/src/scanner.js index 6c709c8..feafd61 100644 --- a/src/scanner.js +++ b/src/scanner.js @@ -61,6 +61,7 @@ function createScanner(pixelCapturer) { $drawable.style.width = `${zoom * 100}%`; $drawable.style.transform = `translate(${translate}%, ${translate}%)`; + $drawable.style.position = 'absolute'; $viewport.style.paddingBottom = `${(viewport.height * 100 / viewport.width).toFixed(5)}%`; $viewport.style.overflow = "hidden"; $viewport.style.height = 0; @@ -233,8 +234,6 @@ function createScanner(pixelCapturer) { return pixelCapturer.grabFrameData({clipping: calculateClipping}) .then((bitmap) => { if (bitmap) { - //console.log(bitmap.dimensions); - // adjust image size! if (availableWorker) { availableWorker.imageData = bitmap.data; availableWorker.dimensions = bitmap.dimensions; @@ -428,13 +427,16 @@ function createScanner(pixelCapturer) { if (imageWrapper) { _onUIThread = false; initBuffers(imageWrapper); - return Promise.resolve(); - } else { - adjustWorkerPool(0); - return setup(_config); } + return Promise.resolve(); }, start: function() { + if (_onUIThread) { + adjustWorkerPool(0); + return source.waitUntilReady() + .then(setup.bind(null, _config)) + .then(start); + } start(); }, isRunning: function() { @@ -447,6 +449,7 @@ function createScanner(pixelCapturer) { if (source.getScope() === Scope.INTERNAL) { source.stop(); } + _events.publish("stopped"); }, applyConfig(newConfig) { return this.init(newConfig); @@ -454,8 +457,11 @@ function createScanner(pixelCapturer) { pause: function() { _stopped = true; }, - subscribe(eventName, callback) { - _events.subscribe(eventName, callback); + subscribe(eventName, callback, once = false) { + if (!once) { + return _events.subscribe(eventName, callback); + } + _events.once(eventName, callback); }, unsubscribe(eventName, callback) { _events.unsubscribe(eventName, callback); @@ -474,7 +480,7 @@ function createScanner(pixelCapturer) { } return reject(result); }, true); - start(); + this.start(); }); }, canvas: _canvasContainer