Remove canplay logic

The manual call of play() just after setting the video stream introduced in
the previous commit seems to suffice without the need to wait for the
canplay event.

Co-authored-by: shrpne <shrpne@gmail.com>
This commit is contained in:
Danielandshrpne committed 2020-07-05 13:45:29 +02:00
1 parent 54e66c5a82
commit b3713f2b38
3 files changed
+12 -20

No files matched your search

+11 -11
View File
@@ -1,12 +1,12 @@
class e{static hasCamera(){return!!navigator.mediaDevices&&navigator.mediaDevices.enumerateDevices().then((a)=>a.some((a)=>"videoinput"===a.kind)).catch(()=>!1)}constructor(a,c,b=e.DEFAULT_CANVAS_SIZE){this.$video=a;this.$canvas=document.createElement("canvas");this._onDecode=c;this._paused=this._active=!1;this.$canvas.width=b;this.$canvas.height=b;this._sourceRect={x:0,y:0,width:b,height:b};this._onCanPlay=this._onCanPlay.bind(this);this._onPlay=this._onPlay.bind(this);this._onVisibilityChange=
this._onVisibilityChange.bind(this);this.$video.addEventListener("canplay",this._onCanPlay);this.$video.addEventListener("play",this._onPlay);document.addEventListener("visibilitychange",this._onVisibilityChange);this._qrWorker=new Worker(e.WORKER_PATH)}destroy(){this.$video.removeEventListener("canplay",this._onCanPlay);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="environment";return this._getCameraStream("environment",!0).catch(()=>{a="user";return this._getCameraStream()}).then((c)=>{this.$video.srcObject=
c;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()[0];a&&(a.stop(),this._offTimeout=this.$video.srcObject=null)},300)))}static scanImage(a,c=null,b=null,d=null,f=!1,g=!1){let h=!1,l=new Promise((l,g)=>{b||(b=new Worker(e.WORKER_PATH),h=!0,b.postMessage({type:"inversionMode",
data:"both"}));let n,m,k;m=(a)=>{"qrResult"===a.data.type&&(b.removeEventListener("message",m),b.removeEventListener("error",k),clearTimeout(n),null!==a.data.data?l(a.data.data):g("QR code not found."))};k=(a)=>{b.removeEventListener("message",m);b.removeEventListener("error",k);clearTimeout(n);g("Scanner error: "+(a?a.message||a:"Unknown Error"))};b.addEventListener("message",m);b.addEventListener("error",k);n=setTimeout(()=>k("timeout"),3E3);e._loadImage(a).then((a)=>{a=e._getImageData(a,c,d,f);
b.postMessage({type:"decode",data:a},[a.data.buffer])}).catch(k)});c&&g&&(l=l.catch(()=>e.scanImage(a,null,b,d,f)));return l=l.finally(()=>{h&&b.postMessage({type:"close"})})}setGrayscaleWeights(a,c,b,d=!0){this._qrWorker.postMessage({type:"grayscaleWeights",data:{red:a,green:c,blue:b,useIntegerApproximation:d}})}setInversionMode(a){this._qrWorker.postMessage({type:"inversionMode",data:a})}_onCanPlay(){this._updateSourceRect();this.$video.play()}_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(()=>{e.scanImage(this.$video,this._sourceRect,this._qrWorker,this.$canvas,!0).then(this._onDecode,(a)=>{this._active&&
"QR code not found."!==a&&console.error(a)}).then(()=>this._scanFrame())})}_getCameraStream(a,c=!1){let b=[{width:{min:1024}},{width:{min:768}},{}];a&&(c&&(a={exact:a}),b.forEach((b)=>b.facingMode=a));return this._getMatchingCameraStream(b)}_getMatchingCameraStream(a){return navigator.mediaDevices&&0!==a.length?navigator.mediaDevices.getUserMedia({video:a.shift()}).catch(()=>this._getMatchingCameraStream(a)):Promise.reject("Camera not found.")}_setVideoMirror(a){this.$video.style.transform="scaleX("+
("user"===a?-1:1)+")"}static _getImageData(a,c=null,b=null,d=!1){b=b||document.createElement("canvas");let f=c&&c.x?c.x:0,g=c&&c.y?c.y:0,h=c&&c.width?c.width:a.width||a.videoWidth;c=c&&c.height?c.height:a.height||a.videoHeight;d||b.width===h&&b.height===c||(b.width=h,b.height=c);d=b.getContext("2d",{alpha:!1});d.imageSmoothingEnabled=!1;d.drawImage(a,f,g,h,c,0,0,b.width,b.height);return d.getImageData(0,0,b.width,b.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 URL||"string"===typeof a){let c=new Image;c.src=a instanceof File?URL.createObjectURL(a):a;return e._awaitImageLoad(c).then(()=>{a instanceof File&&URL.revokeObjectURL(c.src);return c})}return Promise.reject("Unsupported image type.")}static _awaitImageLoad(a){return new Promise((c,
b)=>{if(a.complete&&0!==a.naturalWidth)c();else{let d,f;d=()=>{a.removeEventListener("load",d);a.removeEventListener("error",f);c()};f=()=>{a.removeEventListener("load",d);a.removeEventListener("error",f);b("Image load error")};a.addEventListener("load",d);a.addEventListener("error",f)}})}}e.DEFAULT_CANVAS_SIZE=400;e.WORKER_PATH="qr-scanner-worker.min.js";export default e;
class e{static hasCamera(){return!!navigator.mediaDevices&&navigator.mediaDevices.enumerateDevices().then((a)=>a.some((a)=>"videoinput"===a.kind)).catch(()=>!1)}constructor(a,c,b=e.DEFAULT_CANVAS_SIZE){this.$video=a;this.$canvas=document.createElement("canvas");this._onDecode=c;this._paused=this._active=!1;this.$canvas.width=b;this.$canvas.height=b;this._sourceRect={x:0,y:0,width:b,height:b};this._onPlay=this._onPlay.bind(this);this._onVisibilityChange=this._onVisibilityChange.bind(this);
this.$video.addEventListener("play",this._onPlay);document.addEventListener("visibilitychange",this._onVisibilityChange);this._qrWorker=new Worker(e.WORKER_PATH)}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="environment";return this._getCameraStream("environment",!0).catch(()=>{a="user";return this._getCameraStream()}).then((c)=>{this.$video.srcObject=c;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()[0];a&&(a.stop(),this._offTimeout=this.$video.srcObject=null)},300)))}static scanImage(a,c=null,b=null,d=null,f=!1,g=!1){let h=!1,l=new Promise((l,g)=>{b||(b=new Worker(e.WORKER_PATH),h=!0,b.postMessage({type:"inversionMode",data:"both"}));let n,m,k;m=(a)=>{"qrResult"===a.data.type&&(b.removeEventListener("message",m),b.removeEventListener("error",k),clearTimeout(n),
null!==a.data.data?l(a.data.data):g("QR code not found."))};k=(a)=>{b.removeEventListener("message",m);b.removeEventListener("error",k);clearTimeout(n);g("Scanner error: "+(a?a.message||a:"Unknown Error"))};b.addEventListener("message",m);b.addEventListener("error",k);n=setTimeout(()=>k("timeout"),3E3);e._loadImage(a).then((a)=>{a=e._getImageData(a,c,d,f);b.postMessage({type:"decode",data:a},[a.data.buffer])}).catch(k)});c&&g&&(l=l.catch(()=>e.scanImage(a,null,b,d,f)));return l=l.finally(()=>{h&&
b.postMessage({type:"close"})})}setGrayscaleWeights(a,c,b,d=!0){this._qrWorker.postMessage({type:"grayscaleWeights",data:{red:a,green:c,blue:b,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(()=>{e.scanImage(this.$video,this._sourceRect,this._qrWorker,this.$canvas,!0).then(this._onDecode,(a)=>{this._active&&"QR code not found."!==a&&console.error(a)}).then(()=>this._scanFrame())})}_getCameraStream(a,c=!1){let b=[{width:{min:1024}},{width:{min:768}},{}];a&&(c&&(a={exact:a}),
b.forEach((b)=>b.facingMode=a));return this._getMatchingCameraStream(b)}_getMatchingCameraStream(a){return navigator.mediaDevices&&0!==a.length?navigator.mediaDevices.getUserMedia({video:a.shift()}).catch(()=>this._getMatchingCameraStream(a)):Promise.reject("Camera not found.")}_setVideoMirror(a){this.$video.style.transform="scaleX("+("user"===a?-1:1)+")"}static _getImageData(a,c=null,b=null,d=!1){b=b||document.createElement("canvas");let f=c&&c.x?c.x:0,g=c&&c.y?c.y:0,h=c&&c.width?c.width:a.width||
a.videoWidth;c=c&&c.height?c.height:a.height||a.videoHeight;d||b.width===h&&b.height===c||(b.width=h,b.height=c);d=b.getContext("2d",{alpha:!1});d.imageSmoothingEnabled=!1;d.drawImage(a,f,g,h,c,0,0,b.width,b.height);return d.getImageData(0,0,b.width,b.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 URL||"string"===typeof a){let c=new Image;c.src=a instanceof File?URL.createObjectURL(a):a;return e._awaitImageLoad(c).then(()=>{a instanceof File&&URL.revokeObjectURL(c.src);return c})}return Promise.reject("Unsupported image type.")}static _awaitImageLoad(a){return new Promise((c,b)=>{if(a.complete&&0!==a.naturalWidth)c();else{let d,f;d=()=>{a.removeEventListener("load",d);a.removeEventListener("error",f);c()};f=()=>
{a.removeEventListener("load",d);a.removeEventListener("error",f);b("Image load error")};a.addEventListener("load",d);a.addEventListener("error",f)}})}}e.DEFAULT_CANVAS_SIZE=400;e.WORKER_PATH="qr-scanner-worker.min.js";export default e;
//# sourceMappingURL=qr-scanner.min.js.map
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"qr-scanner.min.js","sources":["src/qr-scanner.js"],"sourcesContent":["export default class QrScanner {\n /* async */\n static hasCamera() {\n // note that enumerateDevices can always be called and does not prompt the user for permission. However, device\n // labels are only readable if served via https and an active media stream exists or permanent permission is\n // given. That doesn't matter for us though as we don't require labels.\n return !!navigator.mediaDevices && navigator.mediaDevices.enumerateDevices()\n .then(devices => devices.some(device => device.kind === 'videoinput'))\n .catch(() => false);\n }\n\n constructor(video, onDecode, canvasSize = QrScanner.DEFAULT_CANVAS_SIZE) {\n this.$video = video;\n this.$canvas = document.createElement('canvas');\n this._onDecode = onDecode;\n this._active = false;\n this._paused = false;\n\n this.$canvas.width = canvasSize;\n this.$canvas.height = canvasSize;\n this._sourceRect = {\n x: 0,\n y: 0,\n width: canvasSize,\n height: canvasSize\n };\n\n this._onCanPlay = this._onCanPlay.bind(this);\n this._onPlay = this._onPlay.bind(this);\n this._onVisibilityChange = this._onVisibilityChange.bind(this);\n\n this.$video.addEventListener('canplay', this._onCanPlay);\n this.$video.addEventListener('play', this._onPlay);\n document.addEventListener('visibilitychange', this._onVisibilityChange);\n\n this._qrWorker = new Worker(QrScanner.WORKER_PATH);\n }\n\n destroy() {\n this.$video.removeEventListener('canplay', this._onCanPlay);\n this.$video.removeEventListener('play', this._onPlay);\n document.removeEventListener('visibilitychange', this._onVisibilityChange);\n\n this.stop();\n this._qrWorker.postMessage({\n type: 'close'\n });\n }\n\n /* async */\n start() {\n if (this._active && !this._paused) {\n return Promise.resolve();\n }\n if (window.location.protocol !== 'https:') {\n // warn but try starting the camera anyways\n console.warn('The camera stream is only accessible if the page is transferred via https.');\n }\n this._active = true;\n this._paused = false;\n if (document.hidden) {\n // camera will be started as soon as tab is in foreground\n return Promise.resolve();\n }\n clearTimeout(this._offTimeout);\n this._offTimeout = null;\n if (this.$video.srcObject) {\n // camera stream already/still set\n this.$video.play();\n return Promise.resolve();\n }\n\n let facingMode = 'environment';\n return this._getCameraStream('environment', true)\n .catch(() => {\n // we (probably) don't have an environment camera\n facingMode = 'user';\n return this._getCameraStream(); // throws if camera is not accessible (e.g. due to not https)\n })\n .then(stream => {\n this.$video.srcObject = stream;\n this.$video.play();\n this._setVideoMirror(facingMode);\n })\n .catch(e => {\n this._active = false;\n throw e;\n });\n }\n\n stop() {\n this.pause();\n this._active = false;\n }\n\n pause() {\n this._paused = true;\n if (!this._active) {\n return;\n }\n this.$video.pause();\n if (this._offTimeout) {\n return;\n }\n this._offTimeout = setTimeout(() => {\n const track = this.$video.srcObject && this.$video.srcObject.getTracks()[0];\n if (!track) return;\n track.stop();\n this.$video.srcObject = null;\n this._offTimeout = null;\n }, 300);\n }\n\n /* async */\n static scanImage(imageOrFileOrUrl, sourceRect=null, worker=null, canvas=null, fixedCanvasSize=false,\n alsoTryWithoutSourceRect=false) {\n let createdNewWorker = false;\n let promise = new Promise((resolve, reject) => {\n if (!worker) {\n worker = new Worker(QrScanner.WORKER_PATH);\n createdNewWorker = true;\n worker.postMessage({ type: 'inversionMode', data: 'both' }); // scan inverted color qr codes too\n }\n let timeout, onMessage, onError;\n onMessage = event => {\n if (event.data.type !== 'qrResult') {\n return;\n }\n worker.removeEventListener('message', onMessage);\n worker.removeEventListener('error', onError);\n clearTimeout(timeout);\n if (event.data.data !==Line truncated
{"version":3,"file":"qr-scanner.min.js","sources":["src/qr-scanner.js"],"sourcesContent":["export default class QrScanner {\n /* async */\n static hasCamera() {\n // note that enumerateDevices can always be called and does not prompt the user for permission. However, device\n // labels are only readable if served via https and an active media stream exists or permanent permission is\n // given. That doesn't matter for us though as we don't require labels.\n return !!navigator.mediaDevices && navigator.mediaDevices.enumerateDevices()\n .then(devices => devices.some(device => device.kind === 'videoinput'))\n .catch(() => false);\n }\n\n constructor(video, onDecode, canvasSize = QrScanner.DEFAULT_CANVAS_SIZE) {\n this.$video = video;\n this.$canvas = document.createElement('canvas');\n this._onDecode = onDecode;\n this._active = false;\n this._paused = false;\n\n this.$canvas.width = canvasSize;\n this.$canvas.height = canvasSize;\n this._sourceRect = {\n x: 0,\n y: 0,\n width: canvasSize,\n height: canvasSize\n };\n\n this._onPlay = this._onPlay.bind(this);\n this._onVisibilityChange = this._onVisibilityChange.bind(this);\n\n this.$video.addEventListener('play', this._onPlay);\n document.addEventListener('visibilitychange', this._onVisibilityChange);\n\n this._qrWorker = new Worker(QrScanner.WORKER_PATH);\n }\n\n destroy() {\n this.$video.removeEventListener('play', this._onPlay);\n document.removeEventListener('visibilitychange', this._onVisibilityChange);\n\n this.stop();\n this._qrWorker.postMessage({\n type: 'close'\n });\n }\n\n /* async */\n start() {\n if (this._active && !this._paused) {\n return Promise.resolve();\n }\n if (window.location.protocol !== 'https:') {\n // warn but try starting the camera anyways\n console.warn('The camera stream is only accessible if the page is transferred via https.');\n }\n this._active = true;\n this._paused = false;\n if (document.hidden) {\n // camera will be started as soon as tab is in foreground\n return Promise.resolve();\n }\n clearTimeout(this._offTimeout);\n this._offTimeout = null;\n if (this.$video.srcObject) {\n // camera stream already/still set\n this.$video.play();\n return Promise.resolve();\n }\n\n let facingMode = 'environment';\n return this._getCameraStream('environment', true)\n .catch(() => {\n // we (probably) don't have an environment camera\n facingMode = 'user';\n return this._getCameraStream(); // throws if camera is not accessible (e.g. due to not https)\n })\n .then(stream => {\n this.$video.srcObject = stream;\n this.$video.play();\n this._setVideoMirror(facingMode);\n })\n .catch(e => {\n this._active = false;\n throw e;\n });\n }\n\n stop() {\n this.pause();\n this._active = false;\n }\n\n pause() {\n this._paused = true;\n if (!this._active) {\n return;\n }\n this.$video.pause();\n if (this._offTimeout) {\n return;\n }\n this._offTimeout = setTimeout(() => {\n const track = this.$video.srcObject && this.$video.srcObject.getTracks()[0];\n if (!track) return;\n track.stop();\n this.$video.srcObject = null;\n this._offTimeout = null;\n }, 300);\n }\n\n /* async */\n static scanImage(imageOrFileOrUrl, sourceRect=null, worker=null, canvas=null, fixedCanvasSize=false,\n alsoTryWithoutSourceRect=false) {\n let createdNewWorker = false;\n let promise = new Promise((resolve, reject) => {\n if (!worker) {\n worker = new Worker(QrScanner.WORKER_PATH);\n createdNewWorker = true;\n worker.postMessage({ type: 'inversionMode', data: 'both' }); // scan inverted color qr codes too\n }\n let timeout, onMessage, onError;\n onMessage = event => {\n if (event.data.type !== 'qrResult') {\n return;\n }\n worker.removeEventListener('message', onMessage);\n worker.removeEventListener('error', onError);\n clearTimeout(timeout);\n if (event.data.data !== null) {\n resolve(event.data.data);\n } else {\n reject('QR code not found.');\n }\n };\n onError = (Line truncated
-8
View File
@@ -25,11 +25,9 @@ export default class QrScanner {
height: canvasSize
};
this._onCanPlay = this._onCanPlay.bind(this);
this._onPlay = this._onPlay.bind(this);
this._onVisibilityChange = this._onVisibilityChange.bind(this);
this.$video.addEventListener('canplay', this._onCanPlay);
this.$video.addEventListener('play', this._onPlay);
document.addEventListener('visibilitychange', this._onVisibilityChange);
@@ -37,7 +35,6 @@ export default class QrScanner {
}
destroy() {
this.$video.removeEventListener('canplay', this._onCanPlay);
this.$video.removeEventListener('play', this._onPlay);
document.removeEventListener('visibilitychange', this._onVisibilityChange);
@@ -182,11 +179,6 @@ export default class QrScanner {
});
}
_onCanPlay() {
this._updateSourceRect();
this.$video.play();
}
_onPlay() {
this._updateSourceRect();
this._scanFrame();