Update source rect when video ready

b3713f2 switched to starting to play immediately, without waiting for
a canplay event. The side effect of this is, that the play event (on
which the source rect is measured) is triggered before the video
is ready and the video dimensions known. To circumvent this, this
commit adds an additional source rect update on loadedmetadata.
This commit is contained in:
Daniel
2020-07-12 00:00:56 +02:00
parent ecc04840dd
commit 87336c313b
6 changed files with 46 additions and 30 deletions
+15 -3
View File
@@ -6,9 +6,6 @@
</head>
<body>
<style>
canvas {
display: none;
}
hr {
margin-top: 32px;
}
@@ -32,6 +29,11 @@
<span id="cam-has-flash"></span>
<br>
<video id="qr-video"></video>
<br>
<label>
<input id="show-scan-region" type="checkbox">
Show scan region
</label>
</div>
<div>
<button id="flash-toggle">📸 Flash: <span id="flash-state">off</span></button>
@@ -101,6 +103,16 @@
});
});
// for debugging
window.scanner = scanner;
document.getElementById('show-scan-region').addEventListener('change', (e) => {
const input = e.target;
const label = input.parentNode;
label.parentNode.insertBefore(scanner.$canvas, label.nextSibling);
scanner.$canvas.style.display = input.checked ? 'block' : 'none';
});
document.getElementById('inversion-mode-select').addEventListener('change', event => {
scanner.setInversionMode(event.target.value);
});
+13 -12
View File
@@ -1,14 +1,15 @@
class e{static hasCamera(){return navigator.mediaDevices?navigator.mediaDevices.enumerateDevices().then(a=>a.some(a=>"videoinput"===a.kind)).catch(()=>!1):Promise.resolve(!1)}constructor(a,b,c=this._onDecodeError.bind(this),d=e.DEFAULT_CANVAS_SIZE,f="environment"){this.$video=a;this.$canvas=document.createElement("canvas");this._onDecode=b;this._preferredFacingMode=f;this._flashOn=this._paused=this._active=!1;"number"===typeof c?(d=c,console.warn("You're using a deprecated version of the QrScanner constructor which will be removed in the future")):
this._onDecodeError=c;this.$canvas.width=d;this.$canvas.height=d;this._sourceRect={x:0,y:0,width:d,height:d};this._onPlay=this._onPlay.bind(this);this._onVisibilityChange=this._onVisibilityChange.bind(this);this.$video.playsInline=!0;this.$video.muted=!0;this.$video.disablePictureInPicture=!0;this.$video.addEventListener("play",this._onPlay);document.addEventListener("visibilitychange",this._onVisibilityChange);this._qrWorker=new Worker(e.WORKER_PATH)}hasFlash(){if(!("ImageCapture"in window))return Promise.resolve(!1);
let a=this.$video.srcObject?this.$video.srcObject.getVideoTracks()[0]:null;return a?(new ImageCapture(a)).getPhotoCapabilities().then(a=>a.fillLightMode.includes("flash")).catch(a=>{console.warn(a);return!1}):Promise.reject("Camera not started or not available")}isFlashOn(){return this._flashOn}toggleFlash(){return this._setFlash(!this._flashOn)}turnFlashOff(){return this._setFlash(!1)}turnFlashOn(){return this._setFlash(!0)}destroy(){this.$video.removeEventListener("play",this._onPlay);document.removeEventListener("visibilitychange",
this._onVisibilityChange);this.stop();this._qrWorker.postMessage({type:"close"})}start(){if(this._active&&!this._paused)return Promise.resolve();"https:"!==window.location.protocol&&console.warn("The camera stream is only accessible if the page is transferred via https.");this._active=!0;this._paused=!1;if(document.hidden)return Promise.resolve();clearTimeout(this._offTimeout);this._offTimeout=null;if(this.$video.srcObject)return this.$video.play(),Promise.resolve();let a=this._preferredFacingMode;
return this._getCameraStream(a,!0).catch(()=>{a="environment"===a?"user":"environment";return this._getCameraStream()}).then(b=>{a=this._getFacingMode(b)||a;this.$video.srcObject=b;this.$video.play();this._setVideoMirror(a)}).catch(a=>{this._active=!1;throw a;})}stop(){this.pause();this._active=!1}pause(){this._paused=!0;this._active&&(this.$video.pause(),this._offTimeout||(this._offTimeout=setTimeout(()=>{let a=this.$video.srcObject?this.$video.srcObject.getTracks():[];for(let b of a)b.stop();this._offTimeout=
this.$video.srcObject=null},300)))}static scanImage(a,b=null,c=null,d=null,f=!1,g=!1){let h=!1,l=new Promise((l,g)=>{c||(c=new Worker(e.WORKER_PATH),h=!0,c.postMessage({type:"inversionMode",data:"both"}));let n,m,k;m=a=>{"qrResult"===a.data.type&&(c.removeEventListener("message",m),c.removeEventListener("error",k),clearTimeout(n),null!==a.data.data?l(a.data.data):g(e.NO_QR_CODE_FOUND))};k=a=>{c.removeEventListener("message",m);c.removeEventListener("error",k);clearTimeout(n);g("Scanner error: "+(a?
a.message||a:"Unknown Error"))};c.addEventListener("message",m);c.addEventListener("error",k);n=setTimeout(()=>k("timeout"),1E4);e._loadImage(a).then(a=>{a=e._getImageData(a,b,d,f);c.postMessage({type:"decode",data:a},[a.data.buffer])}).catch(k)});b&&g&&(l=l.catch(()=>e.scanImage(a,null,c,d,f)));return l=l.finally(()=>{h&&c.postMessage({type:"close"})})}setGrayscaleWeights(a,b,c,d=!0){this._qrWorker.postMessage({type:"grayscaleWeights",data:{red:a,green:b,blue:c,useIntegerApproximation:d}})}setInversionMode(a){this._qrWorker.postMessage({type:"inversionMode",
data:a})}_onPlay(){this._updateSourceRect();this._scanFrame()}_onVisibilityChange(){document.hidden?this.pause():this._active&&this.start()}_updateSourceRect(){let a=Math.round(2/3*Math.min(this.$video.videoWidth,this.$video.videoHeight));this._sourceRect.width=this._sourceRect.height=a;this._sourceRect.x=(this.$video.videoWidth-a)/2;this._sourceRect.y=(this.$video.videoHeight-a)/2}_scanFrame(){if(!this._active||this.$video.paused||this.$video.ended)return!1;requestAnimationFrame(()=>{1>=this.$video.readyState?
this._scanFrame():e.scanImage(this.$video,this._sourceRect,this._qrWorker,this.$canvas,!0).then(this._onDecode,a=>{this._active&&this._onDecodeError(a)}).then(()=>this._scanFrame())})}_onDecodeError(a){a!==e.NO_QR_CODE_FOUND&&console.log(a)}_getCameraStream(a,b=!1){let c=[{width:{min:1024}},{width:{min:768}},{}];a&&(b&&(a={exact:a}),c.forEach(b=>b.facingMode=a));return this._getMatchingCameraStream(c)}_getMatchingCameraStream(a){return navigator.mediaDevices&&0!==a.length?navigator.mediaDevices.getUserMedia({video:a.shift()}).catch(()=>
this._getMatchingCameraStream(a)):Promise.reject("Camera not found.")}_setFlash(a){return this.hasFlash().then(b=>b?this.$video.srcObject.getVideoTracks()[0].applyConstraints({advanced:[{torch:a}]}):Promise.reject("No flash available")).then(()=>this._flashOn=a)}_setVideoMirror(a){this.$video.style.transform="scaleX("+("user"===a?-1:1)+")"}_getFacingMode(a){return(a=a.getVideoTracks()[0])?/rear|back|environment/i.test(a.label)?"environment":/front|user|face/i.test(a.label)?"user":null:null}static _getImageData(a,
b=null,c=null,d=!1){c=c||document.createElement("canvas");let f=b&&b.x?b.x:0,g=b&&b.y?b.y:0,h=b&&b.width?b.width:a.width||a.videoWidth;b=b&&b.height?b.height:a.height||a.videoHeight;d||c.width===h&&c.height===b||(c.width=h,c.height=b);d=c.getContext("2d",{alpha:!1});d.imageSmoothingEnabled=!1;d.drawImage(a,f,g,h,b,0,0,c.width,c.height);return d.getImageData(0,0,c.width,c.height)}static _loadImage(a){if(a instanceof HTMLCanvasElement||a instanceof HTMLVideoElement||window.ImageBitmap&&a instanceof
window.ImageBitmap||window.OffscreenCanvas&&a instanceof window.OffscreenCanvas)return Promise.resolve(a);if(a instanceof Image)return e._awaitImageLoad(a).then(()=>a);if(a instanceof File||a instanceof Blob||a instanceof URL||"string"===typeof a){let b=new Image;b.src=a instanceof File||a instanceof Blob?URL.createObjectURL(a):a;return e._awaitImageLoad(b).then(()=>{(a instanceof File||a instanceof Blob)&&URL.revokeObjectURL(b.src);return b})}return Promise.reject("Unsupported image type.")}static _awaitImageLoad(a){return new Promise((b,
c)=>{if(a.complete&&0!==a.naturalWidth)b();else{let d,f;d=()=>{a.removeEventListener("load",d);a.removeEventListener("error",f);b()};f=()=>{a.removeEventListener("load",d);a.removeEventListener("error",f);c("Image load error")};a.addEventListener("load",d);a.addEventListener("error",f)}})}}e.DEFAULT_CANVAS_SIZE=400;e.NO_QR_CODE_FOUND="No QR code found";e.WORKER_PATH="qr-scanner-worker.min.js";export default e
this._onDecodeError=c;this.$canvas.width=d;this.$canvas.height=d;this._sourceRect={x:0,y:0,width:d,height:d};this._updateSourceRect=this._updateSourceRect.bind(this);this._onPlay=this._onPlay.bind(this);this._onVisibilityChange=this._onVisibilityChange.bind(this);this.$video.playsInline=!0;this.$video.muted=!0;this.$video.disablePictureInPicture=!0;this.$video.addEventListener("loadedmetadata",this._updateSourceRect);this.$video.addEventListener("play",this._onPlay);document.addEventListener("visibilitychange",
this._onVisibilityChange);this._qrWorker=new Worker(e.WORKER_PATH)}hasFlash(){if(!("ImageCapture"in window))return Promise.resolve(!1);let a=this.$video.srcObject?this.$video.srcObject.getVideoTracks()[0]:null;return a?(new ImageCapture(a)).getPhotoCapabilities().then(a=>a.fillLightMode.includes("flash")).catch(a=>{console.warn(a);return!1}):Promise.reject("Camera not started or not available")}isFlashOn(){return this._flashOn}toggleFlash(){return this._setFlash(!this._flashOn)}turnFlashOff(){return this._setFlash(!1)}turnFlashOn(){return this._setFlash(!0)}destroy(){this.$video.removeEventListener("loadedmetadata",
this._updateSourceRect);this.$video.removeEventListener("play",this._onPlay);document.removeEventListener("visibilitychange",this._onVisibilityChange);this.stop();this._qrWorker.postMessage({type:"close"})}start(){if(this._active&&!this._paused)return Promise.resolve();"https:"!==window.location.protocol&&console.warn("The camera stream is only accessible if the page is transferred via https.");this._active=!0;this._paused=!1;if(document.hidden)return Promise.resolve();clearTimeout(this._offTimeout);
this._offTimeout=null;if(this.$video.srcObject)return this.$video.play(),Promise.resolve();let a=this._preferredFacingMode;return this._getCameraStream(a,!0).catch(()=>{a="environment"===a?"user":"environment";return this._getCameraStream()}).then(b=>{a=this._getFacingMode(b)||a;this.$video.srcObject=b;this.$video.play();this._setVideoMirror(a)}).catch(a=>{this._active=!1;throw a;})}stop(){this.pause();this._active=!1}pause(){this._paused=!0;this._active&&(this.$video.pause(),this._offTimeout||(this._offTimeout=
setTimeout(()=>{let a=this.$video.srcObject?this.$video.srcObject.getTracks():[];for(let b of a)b.stop();this._offTimeout=this.$video.srcObject=null},300)))}static scanImage(a,b=null,c=null,d=null,f=!1,g=!1){let h=!1,l=new Promise((l,g)=>{c||(c=new Worker(e.WORKER_PATH),h=!0,c.postMessage({type:"inversionMode",data:"both"}));let n,m,k;m=a=>{"qrResult"===a.data.type&&(c.removeEventListener("message",m),c.removeEventListener("error",k),clearTimeout(n),null!==a.data.data?l(a.data.data):g(e.NO_QR_CODE_FOUND))};
k=a=>{c.removeEventListener("message",m);c.removeEventListener("error",k);clearTimeout(n);g("Scanner error: "+(a?a.message||a:"Unknown Error"))};c.addEventListener("message",m);c.addEventListener("error",k);n=setTimeout(()=>k("timeout"),1E4);e._loadImage(a).then(a=>{a=e._getImageData(a,b,d,f);c.postMessage({type:"decode",data:a},[a.data.buffer])}).catch(k)});b&&g&&(l=l.catch(()=>e.scanImage(a,null,c,d,f)));return l=l.finally(()=>{h&&c.postMessage({type:"close"})})}setGrayscaleWeights(a,b,c,d=!0){this._qrWorker.postMessage({type:"grayscaleWeights",
data:{red:a,green:b,blue:c,useIntegerApproximation:d}})}setInversionMode(a){this._qrWorker.postMessage({type:"inversionMode",data:a})}_onPlay(){this._updateSourceRect();this._scanFrame()}_onVisibilityChange(){document.hidden?this.pause():this._active&&this.start()}_updateSourceRect(){let a=Math.round(2/3*Math.min(this.$video.videoWidth,this.$video.videoHeight));this._sourceRect.width=this._sourceRect.height=a;this._sourceRect.x=(this.$video.videoWidth-a)/2;this._sourceRect.y=(this.$video.videoHeight-
a)/2}_scanFrame(){if(!this._active||this.$video.paused||this.$video.ended)return!1;requestAnimationFrame(()=>{1>=this.$video.readyState?this._scanFrame():e.scanImage(this.$video,this._sourceRect,this._qrWorker,this.$canvas,!0).then(this._onDecode,a=>{this._active&&this._onDecodeError(a)}).then(()=>this._scanFrame())})}_onDecodeError(a){a!==e.NO_QR_CODE_FOUND&&console.log(a)}_getCameraStream(a,b=!1){let c=[{width:{min:1024}},{width:{min:768}},{}];a&&(b&&(a={exact:a}),c.forEach(b=>b.facingMode=a));
return this._getMatchingCameraStream(c)}_getMatchingCameraStream(a){return navigator.mediaDevices&&0!==a.length?navigator.mediaDevices.getUserMedia({video:a.shift()}).catch(()=>this._getMatchingCameraStream(a)):Promise.reject("Camera not found.")}_setFlash(a){return this.hasFlash().then(b=>b?this.$video.srcObject.getVideoTracks()[0].applyConstraints({advanced:[{torch:a}]}):Promise.reject("No flash available")).then(()=>this._flashOn=a)}_setVideoMirror(a){this.$video.style.transform="scaleX("+("user"===
a?-1:1)+")"}_getFacingMode(a){return(a=a.getVideoTracks()[0])?/rear|back|environment/i.test(a.label)?"environment":/front|user|face/i.test(a.label)?"user":null:null}static _getImageData(a,b=null,c=null,d=!1){c=c||document.createElement("canvas");let f=b&&b.x?b.x:0,g=b&&b.y?b.y:0,h=b&&b.width?b.width:a.width||a.videoWidth;b=b&&b.height?b.height:a.height||a.videoHeight;d||c.width===h&&c.height===b||(c.width=h,c.height=b);d=c.getContext("2d",{alpha:!1});d.imageSmoothingEnabled=!1;d.drawImage(a,f,g,h,
b,0,0,c.width,c.height);return d.getImageData(0,0,c.width,c.height)}static _loadImage(a){if(a instanceof HTMLCanvasElement||a instanceof HTMLVideoElement||window.ImageBitmap&&a instanceof window.ImageBitmap||window.OffscreenCanvas&&a instanceof window.OffscreenCanvas)return Promise.resolve(a);if(a instanceof Image)return e._awaitImageLoad(a).then(()=>a);if(a instanceof File||a instanceof Blob||a instanceof URL||"string"===typeof a){let b=new Image;b.src=a instanceof File||a instanceof Blob?URL.createObjectURL(a):
a;return e._awaitImageLoad(b).then(()=>{(a instanceof File||a instanceof Blob)&&URL.revokeObjectURL(b.src);return b})}return Promise.reject("Unsupported image type.")}static _awaitImageLoad(a){return new Promise((b,c)=>{if(a.complete&&0!==a.naturalWidth)b();else{let d,f;d=()=>{a.removeEventListener("load",d);a.removeEventListener("error",f);b()};f=()=>{a.removeEventListener("load",d);a.removeEventListener("error",f);c("Image load error")};a.addEventListener("load",d);a.addEventListener("error",f)}})}}
e.DEFAULT_CANVAS_SIZE=400;e.NO_QR_CODE_FOUND="No QR code found";e.WORKER_PATH="qr-scanner-worker.min.js";export default e
//# sourceMappingURL=qr-scanner.min.js.map
File diff suppressed because one or more lines are too long
+13 -13
View File
@@ -1,15 +1,15 @@
'use strict';(function(d,a){"object"===typeof exports&&"undefined"!==typeof module?module.exports=a():"function"===typeof define&&define.amd?define(a):(d=d||self,d.QrScanner=a())})(this,function(){class d{static hasCamera(){return navigator.mediaDevices?navigator.mediaDevices.enumerateDevices().then(a=>a.some(a=>"videoinput"===a.kind)).catch(()=>!1):Promise.resolve(!1)}constructor(a,b,c=this._onDecodeError.bind(this),e=d.DEFAULT_CANVAS_SIZE,f="environment"){this.$video=a;this.$canvas=document.createElement("canvas");
this._onDecode=b;this._preferredFacingMode=f;this._flashOn=this._paused=this._active=!1;"number"===typeof c?(e=c,console.warn("You're using a deprecated version of the QrScanner constructor which will be removed in the future")):this._onDecodeError=c;this.$canvas.width=e;this.$canvas.height=e;this._sourceRect={x:0,y:0,width:e,height:e};this._onPlay=this._onPlay.bind(this);this._onVisibilityChange=this._onVisibilityChange.bind(this);this.$video.playsInline=!0;this.$video.muted=!0;this.$video.disablePictureInPicture=
!0;this.$video.addEventListener("play",this._onPlay);document.addEventListener("visibilitychange",this._onVisibilityChange);this._qrWorker=new Worker(d.WORKER_PATH)}hasFlash(){if(!("ImageCapture"in window))return Promise.resolve(!1);let a=this.$video.srcObject?this.$video.srcObject.getVideoTracks()[0]:null;return a?(new ImageCapture(a)).getPhotoCapabilities().then(a=>a.fillLightMode.includes("flash")).catch(a=>{console.warn(a);return!1}):Promise.reject("Camera not started or not available")}isFlashOn(){return this._flashOn}toggleFlash(){return this._setFlash(!this._flashOn)}turnFlashOff(){return this._setFlash(!1)}turnFlashOn(){return this._setFlash(!0)}destroy(){this.$video.removeEventListener("play",
this._onPlay);document.removeEventListener("visibilitychange",this._onVisibilityChange);this.stop();this._qrWorker.postMessage({type:"close"})}start(){if(this._active&&!this._paused)return Promise.resolve();"https:"!==window.location.protocol&&console.warn("The camera stream is only accessible if the page is transferred via https.");this._active=!0;this._paused=!1;if(document.hidden)return Promise.resolve();clearTimeout(this._offTimeout);this._offTimeout=null;if(this.$video.srcObject)return this.$video.play(),
Promise.resolve();let a=this._preferredFacingMode;return this._getCameraStream(a,!0).catch(()=>{a="environment"===a?"user":"environment";return this._getCameraStream()}).then(b=>{a=this._getFacingMode(b)||a;this.$video.srcObject=b;this.$video.play();this._setVideoMirror(a)}).catch(a=>{this._active=!1;throw a;})}stop(){this.pause();this._active=!1}pause(){this._paused=!0;this._active&&(this.$video.pause(),this._offTimeout||(this._offTimeout=setTimeout(()=>{let a=this.$video.srcObject?this.$video.srcObject.getTracks():
[];for(let b of a)b.stop();this._offTimeout=this.$video.srcObject=null},300)))}static scanImage(a,b=null,c=null,e=null,f=!1,g=!1){let h=!1,l=new Promise((l,g)=>{c||(c=new Worker(d.WORKER_PATH),h=!0,c.postMessage({type:"inversionMode",data:"both"}));let n,m,k;m=a=>{"qrResult"===a.data.type&&(c.removeEventListener("message",m),c.removeEventListener("error",k),clearTimeout(n),null!==a.data.data?l(a.data.data):g(d.NO_QR_CODE_FOUND))};k=a=>{c.removeEventListener("message",m);c.removeEventListener("error",
k);clearTimeout(n);g("Scanner error: "+(a?a.message||a:"Unknown Error"))};c.addEventListener("message",m);c.addEventListener("error",k);n=setTimeout(()=>k("timeout"),1E4);d._loadImage(a).then(a=>{a=d._getImageData(a,b,e,f);c.postMessage({type:"decode",data:a},[a.data.buffer])}).catch(k)});b&&g&&(l=l.catch(()=>d.scanImage(a,null,c,e,f)));return l=l.finally(()=>{h&&c.postMessage({type:"close"})})}setGrayscaleWeights(a,b,c,e=!0){this._qrWorker.postMessage({type:"grayscaleWeights",data:{red:a,green:b,
blue:c,useIntegerApproximation:e}})}setInversionMode(a){this._qrWorker.postMessage({type:"inversionMode",data:a})}_onPlay(){this._updateSourceRect();this._scanFrame()}_onVisibilityChange(){document.hidden?this.pause():this._active&&this.start()}_updateSourceRect(){let a=Math.round(2/3*Math.min(this.$video.videoWidth,this.$video.videoHeight));this._sourceRect.width=this._sourceRect.height=a;this._sourceRect.x=(this.$video.videoWidth-a)/2;this._sourceRect.y=(this.$video.videoHeight-a)/2}_scanFrame(){if(!this._active||
this.$video.paused||this.$video.ended)return!1;requestAnimationFrame(()=>{1>=this.$video.readyState?this._scanFrame():d.scanImage(this.$video,this._sourceRect,this._qrWorker,this.$canvas,!0).then(this._onDecode,a=>{this._active&&this._onDecodeError(a)}).then(()=>this._scanFrame())})}_onDecodeError(a){a!==d.NO_QR_CODE_FOUND&&console.log(a)}_getCameraStream(a,b=!1){let c=[{width:{min:1024}},{width:{min:768}},{}];a&&(b&&(a={exact:a}),c.forEach(b=>b.facingMode=a));return this._getMatchingCameraStream(c)}_getMatchingCameraStream(a){return navigator.mediaDevices&&
0!==a.length?navigator.mediaDevices.getUserMedia({video:a.shift()}).catch(()=>this._getMatchingCameraStream(a)):Promise.reject("Camera not found.")}_setFlash(a){return this.hasFlash().then(b=>b?this.$video.srcObject.getVideoTracks()[0].applyConstraints({advanced:[{torch:a}]}):Promise.reject("No flash available")).then(()=>this._flashOn=a)}_setVideoMirror(a){this.$video.style.transform="scaleX("+("user"===a?-1:1)+")"}_getFacingMode(a){return(a=a.getVideoTracks()[0])?/rear|back|environment/i.test(a.label)?
"environment":/front|user|face/i.test(a.label)?"user":null:null}static _getImageData(a,b=null,c=null,e=!1){c=c||document.createElement("canvas");let d=b&&b.x?b.x:0,g=b&&b.y?b.y:0,h=b&&b.width?b.width:a.width||a.videoWidth;b=b&&b.height?b.height:a.height||a.videoHeight;e||c.width===h&&c.height===b||(c.width=h,c.height=b);e=c.getContext("2d",{alpha:!1});e.imageSmoothingEnabled=!1;e.drawImage(a,d,g,h,b,0,0,c.width,c.height);return e.getImageData(0,0,c.width,c.height)}static _loadImage(a){if(a instanceof
HTMLCanvasElement||a instanceof HTMLVideoElement||window.ImageBitmap&&a instanceof window.ImageBitmap||window.OffscreenCanvas&&a instanceof window.OffscreenCanvas)return Promise.resolve(a);if(a instanceof Image)return d._awaitImageLoad(a).then(()=>a);if(a instanceof File||a instanceof Blob||a instanceof URL||"string"===typeof a){let b=new Image;b.src=a instanceof File||a instanceof Blob?URL.createObjectURL(a):a;return d._awaitImageLoad(b).then(()=>{(a instanceof File||a instanceof Blob)&&URL.revokeObjectURL(b.src);
return b})}return Promise.reject("Unsupported image type.")}static _awaitImageLoad(a){return new Promise((b,c)=>{if(a.complete&&0!==a.naturalWidth)b();else{let d,f;d=()=>{a.removeEventListener("load",d);a.removeEventListener("error",f);b()};f=()=>{a.removeEventListener("load",d);a.removeEventListener("error",f);c("Image load error")};a.addEventListener("load",d);a.addEventListener("error",f)}})}}d.DEFAULT_CANVAS_SIZE=400;d.NO_QR_CODE_FOUND="No QR code found";d.WORKER_PATH="qr-scanner-worker.min.js";
return d})
this._onDecode=b;this._preferredFacingMode=f;this._flashOn=this._paused=this._active=!1;"number"===typeof c?(e=c,console.warn("You're using a deprecated version of the QrScanner constructor which will be removed in the future")):this._onDecodeError=c;this.$canvas.width=e;this.$canvas.height=e;this._sourceRect={x:0,y:0,width:e,height:e};this._updateSourceRect=this._updateSourceRect.bind(this);this._onPlay=this._onPlay.bind(this);this._onVisibilityChange=this._onVisibilityChange.bind(this);this.$video.playsInline=
!0;this.$video.muted=!0;this.$video.disablePictureInPicture=!0;this.$video.addEventListener("loadedmetadata",this._updateSourceRect);this.$video.addEventListener("play",this._onPlay);document.addEventListener("visibilitychange",this._onVisibilityChange);this._qrWorker=new Worker(d.WORKER_PATH)}hasFlash(){if(!("ImageCapture"in window))return Promise.resolve(!1);let a=this.$video.srcObject?this.$video.srcObject.getVideoTracks()[0]:null;return a?(new ImageCapture(a)).getPhotoCapabilities().then(a=>a.fillLightMode.includes("flash")).catch(a=>
{console.warn(a);return!1}):Promise.reject("Camera not started or not available")}isFlashOn(){return this._flashOn}toggleFlash(){return this._setFlash(!this._flashOn)}turnFlashOff(){return this._setFlash(!1)}turnFlashOn(){return this._setFlash(!0)}destroy(){this.$video.removeEventListener("loadedmetadata",this._updateSourceRect);this.$video.removeEventListener("play",this._onPlay);document.removeEventListener("visibilitychange",this._onVisibilityChange);this.stop();this._qrWorker.postMessage({type:"close"})}start(){if(this._active&&
!this._paused)return Promise.resolve();"https:"!==window.location.protocol&&console.warn("The camera stream is only accessible if the page is transferred via https.");this._active=!0;this._paused=!1;if(document.hidden)return Promise.resolve();clearTimeout(this._offTimeout);this._offTimeout=null;if(this.$video.srcObject)return this.$video.play(),Promise.resolve();let a=this._preferredFacingMode;return this._getCameraStream(a,!0).catch(()=>{a="environment"===a?"user":"environment";return this._getCameraStream()}).then(b=>
{a=this._getFacingMode(b)||a;this.$video.srcObject=b;this.$video.play();this._setVideoMirror(a)}).catch(a=>{this._active=!1;throw a;})}stop(){this.pause();this._active=!1}pause(){this._paused=!0;this._active&&(this.$video.pause(),this._offTimeout||(this._offTimeout=setTimeout(()=>{let a=this.$video.srcObject?this.$video.srcObject.getTracks():[];for(let b of a)b.stop();this._offTimeout=this.$video.srcObject=null},300)))}static scanImage(a,b=null,c=null,e=null,f=!1,g=!1){let h=!1,l=new Promise((l,g)=>
{c||(c=new Worker(d.WORKER_PATH),h=!0,c.postMessage({type:"inversionMode",data:"both"}));let n,m,k;m=a=>{"qrResult"===a.data.type&&(c.removeEventListener("message",m),c.removeEventListener("error",k),clearTimeout(n),null!==a.data.data?l(a.data.data):g(d.NO_QR_CODE_FOUND))};k=a=>{c.removeEventListener("message",m);c.removeEventListener("error",k);clearTimeout(n);g("Scanner error: "+(a?a.message||a:"Unknown Error"))};c.addEventListener("message",m);c.addEventListener("error",k);n=setTimeout(()=>k("timeout"),
1E4);d._loadImage(a).then(a=>{a=d._getImageData(a,b,e,f);c.postMessage({type:"decode",data:a},[a.data.buffer])}).catch(k)});b&&g&&(l=l.catch(()=>d.scanImage(a,null,c,e,f)));return l=l.finally(()=>{h&&c.postMessage({type:"close"})})}setGrayscaleWeights(a,b,c,e=!0){this._qrWorker.postMessage({type:"grayscaleWeights",data:{red:a,green:b,blue:c,useIntegerApproximation:e}})}setInversionMode(a){this._qrWorker.postMessage({type:"inversionMode",data:a})}_onPlay(){this._updateSourceRect();this._scanFrame()}_onVisibilityChange(){document.hidden?
this.pause():this._active&&this.start()}_updateSourceRect(){let a=Math.round(2/3*Math.min(this.$video.videoWidth,this.$video.videoHeight));this._sourceRect.width=this._sourceRect.height=a;this._sourceRect.x=(this.$video.videoWidth-a)/2;this._sourceRect.y=(this.$video.videoHeight-a)/2}_scanFrame(){if(!this._active||this.$video.paused||this.$video.ended)return!1;requestAnimationFrame(()=>{1>=this.$video.readyState?this._scanFrame():d.scanImage(this.$video,this._sourceRect,this._qrWorker,this.$canvas,
!0).then(this._onDecode,a=>{this._active&&this._onDecodeError(a)}).then(()=>this._scanFrame())})}_onDecodeError(a){a!==d.NO_QR_CODE_FOUND&&console.log(a)}_getCameraStream(a,b=!1){let c=[{width:{min:1024}},{width:{min:768}},{}];a&&(b&&(a={exact:a}),c.forEach(b=>b.facingMode=a));return this._getMatchingCameraStream(c)}_getMatchingCameraStream(a){return navigator.mediaDevices&&0!==a.length?navigator.mediaDevices.getUserMedia({video:a.shift()}).catch(()=>this._getMatchingCameraStream(a)):Promise.reject("Camera not found.")}_setFlash(a){return this.hasFlash().then(b=>
b?this.$video.srcObject.getVideoTracks()[0].applyConstraints({advanced:[{torch:a}]}):Promise.reject("No flash available")).then(()=>this._flashOn=a)}_setVideoMirror(a){this.$video.style.transform="scaleX("+("user"===a?-1:1)+")"}_getFacingMode(a){return(a=a.getVideoTracks()[0])?/rear|back|environment/i.test(a.label)?"environment":/front|user|face/i.test(a.label)?"user":null:null}static _getImageData(a,b=null,c=null,e=!1){c=c||document.createElement("canvas");let d=b&&b.x?b.x:0,g=b&&b.y?b.y:0,h=b&&
b.width?b.width:a.width||a.videoWidth;b=b&&b.height?b.height:a.height||a.videoHeight;e||c.width===h&&c.height===b||(c.width=h,c.height=b);e=c.getContext("2d",{alpha:!1});e.imageSmoothingEnabled=!1;e.drawImage(a,d,g,h,b,0,0,c.width,c.height);return e.getImageData(0,0,c.width,c.height)}static _loadImage(a){if(a instanceof HTMLCanvasElement||a instanceof HTMLVideoElement||window.ImageBitmap&&a instanceof window.ImageBitmap||window.OffscreenCanvas&&a instanceof window.OffscreenCanvas)return Promise.resolve(a);
if(a instanceof Image)return d._awaitImageLoad(a).then(()=>a);if(a instanceof File||a instanceof Blob||a instanceof URL||"string"===typeof a){let b=new Image;b.src=a instanceof File||a instanceof Blob?URL.createObjectURL(a):a;return d._awaitImageLoad(b).then(()=>{(a instanceof File||a instanceof Blob)&&URL.revokeObjectURL(b.src);return b})}return Promise.reject("Unsupported image type.")}static _awaitImageLoad(a){return new Promise((b,c)=>{if(a.complete&&0!==a.naturalWidth)b();else{let d,f;d=()=>
{a.removeEventListener("load",d);a.removeEventListener("error",f);b()};f=()=>{a.removeEventListener("load",d);a.removeEventListener("error",f);c("Image load error")};a.addEventListener("load",d);a.addEventListener("error",f)}})}}d.DEFAULT_CANVAS_SIZE=400;d.NO_QR_CODE_FOUND="No QR code found";d.WORKER_PATH="qr-scanner-worker.min.js";return d})
//# sourceMappingURL=qr-scanner.umd.min.js.map
File diff suppressed because one or more lines are too long
+3
View File
@@ -43,6 +43,7 @@ export default class QrScanner {
height: canvasSize
};
this._updateSourceRect = this._updateSourceRect.bind(this);
this._onPlay = this._onPlay.bind(this);
this._onVisibilityChange = this._onVisibilityChange.bind(this);
@@ -53,6 +54,7 @@ export default class QrScanner {
// includes no audio, but just to be safe.
this.$video.muted = true;
this.$video.disablePictureInPicture = true;
this.$video.addEventListener('loadedmetadata', this._updateSourceRect);
this.$video.addEventListener('play', this._onPlay);
document.addEventListener('visibilitychange', this._onVisibilityChange);
@@ -101,6 +103,7 @@ export default class QrScanner {
}
destroy() {
this.$video.removeEventListener('loadedmetadata', this._updateSourceRect);
this.$video.removeEventListener('play', this._onPlay);
document.removeEventListener('visibilitychange', this._onVisibilityChange);