This is what I use. Works well.
import jsQR from "jsqr";
...
@ViewChild('video', { static: false }) video: ElementRef;
@ViewChild('canvas', { static: false }) canvas: ElementRef;
canvasElement: any;
videoElement: any;
canvasContext: any;
ngAfterViewInit() {
this.canvasElement = this.canvas.nativeElement;
this.canvasContext = this.canvasElement.getContext('2d');
this.videoElement = this.video.nativeElement;
}
async startScan() {
// Not working on iOS standalone mode!
const stream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: 'environment' }
});
this.videoElement.srcObject = stream;
// Required for Safari
this.videoElement.setAttribute('playsinline', true);
this.loading = await this.loadingController.create({});
await this.loading.present();
this.videoElement.play();
requestAnimationFrame(this.scan.bind(this));
}
async scan() {
if (this.videoElement.readyState === this.videoElement.HAVE_ENOUGH_DATA) {
if (this.loading) {
await this.loading.dismiss();
this.loading = null;
this.scanActive = true;
}
this.canvasElement.height = this.videoElement.videoHeight;
this.canvasElement.width = this.videoElement.videoWidth;
this.canvasContext.drawImage(
this.videoElement,
0,
0,
this.canvasElement.width,
this.canvasElement.height
);
const imageData = this.canvasContext.getImageData(
0,
0,
this.canvasElement.width,
this.canvasElement.height
);
const code = jsQR(imageData.data, imageData.width, imageData.height, {
inversionAttempts: 'dontInvert'
});
if (code) {
this.scanActive = false;
this.scanResult = code.data;
this.showQrToast();
} else {
if (this.scanActive) {
requestAnimationFrame(this.scan.bind(this));
}
}
} else {
requestAnimationFrame(this.scan.bind(this));
}
}
page
<ion-button expand="block" (click)="startScan()" *ngIf="!scanActive && !scanResult">
<ion-icon slot="start" name="qr-code-outline"></ion-icon>
Start Scanning
</ion-button>
<ion-button expand="block" (click)="reset()" color="warning" *ngIf="scanResult">
<ion-icon slot="start" name="refresh"></ion-icon>
Reset
</ion-button>
<!-- Shows our camera stream -->
<video #video [hidden]="!scanActive" width="100%"></video>
<!-- Used to render the camera stream images -->
<canvas #canvas hidden></canvas>
<!-- Stop our scanner preview if active -->
<ion-button expand="block" (click)="stopScan()" color="danger" *ngIf="scanActive">
<ion-icon slot="start" name="close"></ion-icon>
Stop Scanning
</ion-button>