|
|
|
@ -1,7 +1,13 @@
|
|
|
|
|
import { Options } from 'jsqr';
|
|
|
|
|
import jsQR from 'jsqr';
|
|
|
|
|
import jsQR, { QRCode, Options } from 'jsqr';
|
|
|
|
|
|
|
|
|
|
class QrcodeDecoder {
|
|
|
|
|
export type CodeResult = QRCode | null;
|
|
|
|
|
|
|
|
|
|
const videoSize = {
|
|
|
|
|
width: { min: 360, ideal: 720, max: 1080 },
|
|
|
|
|
height: { min: 360, ideal: 720, max: 1080 },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export class QrcodeDecoder {
|
|
|
|
|
timerCapture: null | NodeJS.Timeout;
|
|
|
|
|
canvasElem: null | HTMLCanvasElement;
|
|
|
|
|
gCtx: null | CanvasRenderingContext2D;
|
|
|
|
@ -19,7 +25,8 @@ class QrcodeDecoder {
|
|
|
|
|
this.videoElem = null;
|
|
|
|
|
this.getUserMediaHandler = null;
|
|
|
|
|
this.videoConstraints = {
|
|
|
|
|
video: true,
|
|
|
|
|
// default use rear camera
|
|
|
|
|
video: { ...videoSize, facingMode: { exact: 'environment' } },
|
|
|
|
|
audio: false,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@ -94,7 +101,10 @@ class QrcodeDecoder {
|
|
|
|
|
* inversionAttempts - (attemptBoth (default), dontInvert, onlyInvert, or invertFirst)
|
|
|
|
|
* refer to jsqr options: https://github.com/cozmo/jsQR
|
|
|
|
|
*/
|
|
|
|
|
async _captureToCanvas(videoElem: HTMLVideoElement, options: Options) {
|
|
|
|
|
async _captureToCanvas(
|
|
|
|
|
videoElem: HTMLVideoElement,
|
|
|
|
|
options: Options,
|
|
|
|
|
): Promise<CodeResult> {
|
|
|
|
|
if (this.timerCapture) {
|
|
|
|
|
clearTimeout(this.timerCapture);
|
|
|
|
|
}
|
|
|
|
@ -136,7 +146,7 @@ class QrcodeDecoder {
|
|
|
|
|
|
|
|
|
|
const result = await proms();
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
return result as CodeResult;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -149,7 +159,10 @@ class QrcodeDecoder {
|
|
|
|
|
* inversionAttempts - (attemptBoth (default), dontInvert, onlyInvert, or invertFirst)
|
|
|
|
|
* refer to jsqr options: https://github.com/cozmo/jsQR
|
|
|
|
|
*/
|
|
|
|
|
async decodeFromCamera(videoElem: HTMLVideoElement, options = {}) {
|
|
|
|
|
async decodeFromCamera(
|
|
|
|
|
videoElem: HTMLVideoElement,
|
|
|
|
|
options: any = {},
|
|
|
|
|
): Promise<CodeResult> {
|
|
|
|
|
const opts = {
|
|
|
|
|
...this.defaultOption,
|
|
|
|
|
...options,
|
|
|
|
@ -160,10 +173,29 @@ class QrcodeDecoder {
|
|
|
|
|
throw new Error("Couldn't get video from camera");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let stream: MediaStream;
|
|
|
|
|
try {
|
|
|
|
|
const stream = await navigator.mediaDevices.getUserMedia(
|
|
|
|
|
this.videoConstraints,
|
|
|
|
|
);
|
|
|
|
|
stream = await navigator.mediaDevices.getUserMedia(this.videoConstraints);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
if ((e as OverconstrainedError).name === 'OverconstrainedError') {
|
|
|
|
|
console.log('[OverconstrainedError] Can not use rear camera.');
|
|
|
|
|
|
|
|
|
|
stream = await navigator.mediaDevices.getUserMedia({
|
|
|
|
|
video: {
|
|
|
|
|
...videoSize,
|
|
|
|
|
...{
|
|
|
|
|
width: opts.width,
|
|
|
|
|
height: opts.height,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
audio: false,
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
throw e;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (stream) {
|
|
|
|
|
videoElem.srcObject = stream;
|
|
|
|
|
// videoElem.src = window.URL.createObjectURL(stream);
|
|
|
|
|
this.videoElem = videoElem;
|
|
|
|
@ -171,9 +203,9 @@ class QrcodeDecoder {
|
|
|
|
|
|
|
|
|
|
const code = await this.decodeFromVideo(videoElem, opts);
|
|
|
|
|
return code;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
throw e;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -184,7 +216,10 @@ class QrcodeDecoder {
|
|
|
|
|
* inversionAttempts - (attemptBoth (default), dontInvert, onlyInvert, or invertFirst)
|
|
|
|
|
* refer to jsqr options: https://github.com/cozmo/jsQR
|
|
|
|
|
*/
|
|
|
|
|
async decodeFromVideo(videoElem: HTMLVideoElement, options = {}) {
|
|
|
|
|
async decodeFromVideo(
|
|
|
|
|
videoElem: HTMLVideoElement,
|
|
|
|
|
options = {},
|
|
|
|
|
): Promise<CodeResult> {
|
|
|
|
|
const opts = {
|
|
|
|
|
...this.defaultOption,
|
|
|
|
|
...options,
|
|
|
|
@ -206,20 +241,16 @@ class QrcodeDecoder {
|
|
|
|
|
* refer to jsqr options: https://github.com/cozmo/jsQR
|
|
|
|
|
*/
|
|
|
|
|
async decodeFromImage(
|
|
|
|
|
img: HTMLImageElement,
|
|
|
|
|
img: HTMLImageElement | string,
|
|
|
|
|
options: { crossOrigin?: string } = {},
|
|
|
|
|
) {
|
|
|
|
|
): Promise<CodeResult> {
|
|
|
|
|
let imgDom: HTMLImageElement | null = null;
|
|
|
|
|
const opts = {
|
|
|
|
|
...this.defaultOption,
|
|
|
|
|
...options,
|
|
|
|
|
};
|
|
|
|
|
if (+img.nodeType > 0) {
|
|
|
|
|
if (!img.src) {
|
|
|
|
|
throw new Error('The ImageElement must contain a src');
|
|
|
|
|
}
|
|
|
|
|
imgDom = img;
|
|
|
|
|
} else if (typeof img === 'string') {
|
|
|
|
|
|
|
|
|
|
if (typeof img === 'string') {
|
|
|
|
|
imgDom = document.createElement('img');
|
|
|
|
|
if (options.crossOrigin) {
|
|
|
|
|
imgDom.crossOrigin = options.crossOrigin;
|
|
|
|
@ -230,6 +261,11 @@ class QrcodeDecoder {
|
|
|
|
|
imgDom!.onload = () => resolve(true);
|
|
|
|
|
});
|
|
|
|
|
await proms();
|
|
|
|
|
} else if (+img.nodeType > 0) {
|
|
|
|
|
if (!img.src) {
|
|
|
|
|
throw new Error('The ImageElement must contain a src');
|
|
|
|
|
}
|
|
|
|
|
imgDom = img;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let code = null;
|
|
|
|
@ -256,7 +292,7 @@ class QrcodeDecoder {
|
|
|
|
|
return code;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|