UNPKG

@microblink/blinkid-imagecapture-in-browser-sdk

Version:

A smart image capturing library for WebAssembly-enabled browsers.

636 lines (635 loc) 23.3 kB
/** * Copyright (c) Microblink Ltd. All rights reserved. */ import { Component, Element, Event, Host, h, Method, Prop, State, Watch } from '@stencil/core'; import { CameraExperience, CameraExperienceState, CameraExperienceStateDuration } from '../../../utils/data-structures'; import { setWebComponentParts, classNames, getWebComponentParts } from '../../../utils/generic.helpers'; import * as Utils from './mb-camera-experience.utils'; export class MbCameraExperience { constructor() { this.cameraCursorBarcodeClassName = 'rectangle'; this.cameraCursorIdentityCardClassName = 'reticle'; this.cameraCursorPaymentCardClassName = 'rectangle'; this.cameraMessageIdentityCardClassName = 'message'; this.cameraStateChangeId = 0; this.cameraStateInProgress = false; this.flipCameraStateInProgress = false; /** * Configure camera experience state timeout durations */ this.cameraExperienceStateDurations = null; /** * Unless specifically granted by your license key, you are not allowed to * modify or remove the Microblink logo displayed on the bottom of the camera * overlay. */ this.showOverlay = true; /** * Camera horizontal state passed from root component. * * Horizontal camera image can be mirrored */ this.cameraFlipped = false; /** * Show scanning line on camera */ this.showScanningLine = false; /** * Show camera feedback message on camera for Barcode scanning */ this.showCameraFeedbackBarcodeMessage = false; this.clearIsCameraActive = false; } apiStateHandler(apiState, _oldValue) { if (apiState === '' && (this.type === CameraExperience.CardSingleSide || this.type === CameraExperience.CardMultiSide)) this.cardIdentityElement.classList.add('visible'); else this.cardIdentityElement.classList.remove('visible'); } /** * Change active camera. */ async setActiveCamera(cameraId) { this.cameraToolbar.setActiveCamera(cameraId); } /** * Populate list of camera devices. */ async populateCameraDevices() { await this.cameraToolbar.populateCameraDevices(); } /** * Method is exposed outside which allow us to control Camera Flip state from parent component. */ async setCameraFlipState(isFlipped) { this.cameraFlipped = isFlipped; } /** * Set camera state which includes animation and message. */ setState(state, isBackSide = false, force = false) { return new Promise((resolve) => { if (!force && (!state || this.cameraStateInProgress || this.flipCameraStateInProgress)) { resolve(); return; } this.cameraStateInProgress = true; let cameraStateChangeId = this.cameraStateChangeId + 1; this.cameraStateChangeId = cameraStateChangeId; if (state === CameraExperienceState.Flip) { this.flipCameraStateInProgress = true; } const stateClass = Utils.getStateClass(state); switch (this.type) { case CameraExperience.CardSingleSide: case CameraExperience.CardMultiSide: this.cameraCursorIdentityCardClassName = `reticle ${stateClass}`; break; case CameraExperience.Barcode: stateClass === 'is-detection' && this.showScanningLine ? this.scanningLineBarcodeClassName = 'is-active' : this.scanningLineBarcodeClassName = ''; this.cameraCursorBarcodeClassName = `rectangle ${stateClass}`; break; case CameraExperience.PaymentCard: stateClass === 'is-default' && this.showScanningLine ? this.scanningLinePaymentCardClassName = 'is-active' : this.scanningLinePaymentCardClassName = ''; this.cameraCursorPaymentCardClassName = `rectangle ${stateClass}`; break; } this.setMessage(state, isBackSide, this.type); window.setTimeout(() => { if (this.flipCameraStateInProgress && state === CameraExperienceState.Flip) { this.flipCameraStateInProgress = false; } if (this.cameraStateChangeId === cameraStateChangeId) { this.cameraStateInProgress = false; } resolve(); }, this.getCameraExperienceStateDuration(state)); }); } getCameraExperienceStateDuration(state) { return this.cameraExperienceStateDurations ? this.getStateDurationFromUserInput(state) : CameraExperienceStateDuration.get(state); } getStateDurationFromUserInput(state) { const cameraExperienceStateDurationMap = new Map(Object.entries(this.cameraExperienceStateDurations)); const stateAdjusted = state[0].toLocaleLowerCase() + state.slice(1); const duration = cameraExperienceStateDurationMap.get(stateAdjusted); return duration || CameraExperienceStateDuration.get(state); } /** * Set camera state to initial method. */ resetState() { return new Promise((resolve) => { // Reset flags this.cameraStateChangeId = 0; this.cameraStateInProgress = false; this.flipCameraStateInProgress = false; // Reset DOM while (this.cameraMessageIdentityCard.firstChild) { this.cameraMessageIdentityCard.removeChild(this.cameraMessageIdentityCard.firstChild); } while (this.cameraMessagePaymentCard.firstChild) { this.cameraMessagePaymentCard.removeChild(this.cameraMessagePaymentCard.firstChild); } while (this.cameraMessageBarcode.firstChild) { this.cameraMessageBarcode.removeChild(this.cameraMessageBarcode.firstChild); } resolve(); }); } flipCamera() { this.flipCameraAction.emit(); } handleStop() { this.close.emit(); } setMessage(state, isBackSide, type) { const message = this.getStateMessage(state, isBackSide, type); switch (type) { case CameraExperience.CardSingleSide: case CameraExperience.CardMultiSide: while (this.cameraMessageIdentityCard.firstChild) { this.cameraMessageIdentityCard.removeChild(this.cameraMessageIdentityCard.firstChild); } if (message) { this.cameraMessageIdentityCard.appendChild(message); } this.cameraMessageIdentityCardClassName = message && message !== null ? 'message is-active' : 'message'; break; case CameraExperience.PaymentCard: while (this.cameraMessagePaymentCard.firstChild) { this.cameraMessagePaymentCard.removeChild(this.cameraMessagePaymentCard.firstChild); } if (message) { this.cameraMessagePaymentCard.appendChild(message); } this.cameraMessagePaymentCard.setAttribute('class', message && message !== null ? 'message is-active' : 'message'); break; case CameraExperience.Barcode: while (this.cameraMessageBarcode.firstChild) { this.cameraMessageBarcode.removeChild(this.cameraMessageBarcode.firstChild); } if (this.showCameraFeedbackBarcodeMessage) { if (message) { this.cameraMessageBarcode.appendChild(message); } this.cameraMessageBarcode.setAttribute('class', message && message !== null ? 'message is-active' : 'message'); } break; default: // Do nothing } } getStateMessage(state, isBackSide = false, type) { const getStateMessageAsHTML = (message) => { if (message) { const messageArray = typeof message === 'string' ? [message] : message; const children = []; while (messageArray.length) { const sentence = messageArray.shift(); children.push(document.createTextNode(sentence)); if (messageArray.length) { children.push(document.createElement('br')); } } const spanElement = document.createElement('span'); while (children.length) { spanElement.appendChild(children.shift()); } return spanElement; } }; switch (state) { case CameraExperienceState.Default: if (type === CameraExperience.Barcode && this.showCameraFeedbackBarcodeMessage) { return getStateMessageAsHTML(this.translationService.i('camera-feedback-barcode-message')); } const key = isBackSide ? 'camera-feedback-scan-back' : 'camera-feedback-scan-front'; return getStateMessageAsHTML(this.translationService.i(key)); case CameraExperienceState.MoveFarther: return getStateMessageAsHTML(this.translationService.i('camera-feedback-move-farther')); case CameraExperienceState.MoveCloser: return getStateMessageAsHTML(this.translationService.i('camera-feedback-move-closer')); case CameraExperienceState.AdjustAngle: return getStateMessageAsHTML(this.translationService.i('camera-feedback-adjust-angle')); case CameraExperienceState.Flip: return getStateMessageAsHTML(this.translationService.i('camera-feedback-flip')); case CameraExperienceState.Classification: case CameraExperienceState.Detection: return type === CameraExperience.Barcode ? getStateMessageAsHTML(this.translationService.i('camera-feedback-barcode-message')) : null; case CameraExperienceState.Done: case CameraExperienceState.DoneAll: default: return null; } } handleChangeCameraDevice(camera) { this.changeCameraDevice.emit(camera); } componentDidLoad() { setWebComponentParts(this.hostEl); const parts = getWebComponentParts(this.hostEl.shadowRoot); this.hostEl.setAttribute('exportparts', parts.join(', ')); } render() { return (h(Host, { class: classNames({ 'no-overlay': !this.showOverlay }) }, h("div", { id: "barcode", class: classNames({ visible: this.type === CameraExperience.Barcode }) }, h("div", { class: "rectangle-container" }, h("div", { class: this.cameraCursorBarcodeClassName }, h("div", { class: "rectangle__cursor" }, h("div", { class: "rectangle__el" }), h("div", { class: "rectangle__el" }), h("div", { class: "rectangle__el" }), h("div", { class: "rectangle__el" }), h("div", { class: `scanning-line ${this.scanningLineBarcodeClassName}` }))), h("p", { class: "message", ref: el => this.cameraMessageBarcode = el }))), h("div", { id: "card-identity", ref: (el) => this.cardIdentityElement = el, class: classNames({ visible: this.type === CameraExperience.CardSingleSide || this.type === CameraExperience.CardMultiSide }) }, h("div", { class: "reticle-container" }, h("div", { class: this.cameraCursorIdentityCardClassName }, h("div", { class: "reticle__cursor" }, h("div", { class: "reticle__el" }), h("div", { class: "reticle__el" }), h("div", { class: "reticle__el" }), h("div", { class: "reticle__el" })), h("img", { class: "reticle__done", src: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDgiIGhlaWdodD0iNDgiIHZpZXdCb3g9IjAgMCA0OCA0OCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTIwLjk3MiAzMy40NkMyMC43MDk1IDMzLjQ2MDUgMjAuNDQ5NCAzMy40MDkyIDIwLjIwNjggMzMuMzA5QzE5Ljk2NDEgMzMuMjA4OCAxOS43NDM2IDMzLjA2MTYgMTkuNTU4IDMyLjg3NkwxMS4wNzQgMjQuMzlDMTAuODgyOSAyNC4yMDU2IDEwLjczMDMgMjMuOTg1MSAxMC42MjU0IDIzLjc0MTFDMTAuNTIwNCAyMy40OTcyIDEwLjQ2NSAyMy4yMzQ4IDEwLjQ2MjUgMjIuOTY5MkMxMC40NiAyMi43MDM3IDEwLjUxMDQgMjIuNDQwMyAxMC42MTA4IDIyLjE5NDRDMTAuNzExMiAyMS45NDg2IDEwLjg1OTYgMjEuNzI1MiAxMS4wNDcyIDIxLjUzNzNDMTEuMjM0OSAyMS4zNDkzIDExLjQ1ODEgMjEuMjAwNyAxMS43MDM4IDIxLjA5OTlDMTEuOTQ5NSAyMC45OTkyIDEyLjIxMjggMjAuOTQ4NCAxMi40Nzg0IDIwLjk1MDVDMTIuNzQzOSAyMC45NTI2IDEzLjAwNjQgMjEuMDA3NiAxMy4yNTA1IDIxLjExMjNDMTMuNDk0NiAyMS4yMTY5IDEzLjcxNTQgMjEuMzY5MSAxMy45IDIxLjU2TDIwLjk3IDI4LjYzTDMzLjcgMTUuOTA0QzM0LjA3NSAxNS41Mjg3IDM0LjU4MzggMTUuMzE3OCAzNS4xMTQzIDE1LjMxNzZDMzUuNjQ0OCAxNS4zMTc0IDM2LjE1MzcgMTUuNTI4IDM2LjUyOSAxNS45MDNDMzYuOTA0MyAxNi4yNzggMzcuMTE1MiAxNi43ODY4IDM3LjExNTQgMTcuMzE3M0MzNy4xMTU2IDE3Ljg0NzggMzYuOTA1IDE4LjM1NjcgMzYuNTMgMTguNzMyTDIyLjM4NiAzMi44NzZDMjIuMjAwNCAzMy4wNjE2IDIxLjk3OTkgMzMuMjA4OCAyMS43MzcyIDMzLjMwOUMyMS40OTQ2IDMzLjQwOTIgMjEuMjM0NSAzMy40NjA1IDIwLjk3MiAzMy40NloiIGZpbGw9ImJsYWNrIi8+Cjwvc3ZnPgo=" })), h("p", { class: this.cameraMessageIdentityCardClassName, ref: el => this.cameraMessageIdentityCard = el }))), h("div", { id: "card-payment", class: classNames({ visible: this.type === CameraExperience.PaymentCard }) }, h("div", { class: "rectangle-container" }, h("div", { class: "box wrapper" }), h("div", { class: "box body" }, h("div", { class: "middle-left wrapper" }), h("div", { class: this.cameraCursorPaymentCardClassName }, h("div", { class: "rectangle__cursor" }, h("div", { class: "rectangle__el" }), h("div", { class: "rectangle__el" }), h("div", { class: "rectangle__el" }), h("div", { class: "rectangle__el" }), h("div", { class: `scanning-line ${this.scanningLinePaymentCardClassName}` }))), h("p", { class: "message", ref: el => this.cameraMessagePaymentCard = el }), h("div", { class: "middle-right wrapper" })), h("div", { class: "box wrapper" }))), h("div", { class: "gradient-overlay bottom" }), h("mb-camera-toolbar", { "clear-is-camera-active": this.clearIsCameraActive, "show-close": this.apiState !== "error", "camera-flipped": this.cameraFlipped, onCloseEvent: () => this.handleStop(), onFlipEvent: () => this.flipCamera(), onChangeCameraDevice: (ev) => this.handleChangeCameraDevice(ev.detail), ref: el => this.cameraToolbar = el }))); } static get is() { return "mb-camera-experience"; } static get encapsulation() { return "shadow"; } static get originalStyleUrls() { return { "$": ["mb-camera-experience.scss"] }; } static get styleUrls() { return { "$": ["mb-camera-experience.css"] }; } static get properties() { return { "type": { "type": "string", "mutable": false, "complexType": { "original": "CameraExperience", "resolved": "CameraExperience.Barcode | CameraExperience.CardMultiSide | CameraExperience.CardSingleSide | CameraExperience.PaymentCard", "references": { "CameraExperience": { "location": "import", "path": "../../../utils/data-structures" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Choose desired camera experience.\n\nEach experience type must be implemented in this component." }, "attribute": "type", "reflect": false }, "cameraExperienceStateDurations": { "type": "unknown", "mutable": false, "complexType": { "original": "CameraExperienceTimeoutDurations", "resolved": "CameraExperienceTimeoutDurations", "references": { "CameraExperienceTimeoutDurations": { "location": "import", "path": "../../../utils/data-structures" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Configure camera experience state timeout durations" }, "defaultValue": "null" }, "showOverlay": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Unless specifically granted by your license key, you are not allowed to\nmodify or remove the Microblink logo displayed on the bottom of the camera\noverlay." }, "attribute": "show-overlay", "reflect": false, "defaultValue": "true" }, "translationService": { "type": "unknown", "mutable": false, "complexType": { "original": "TranslationService", "resolved": "TranslationService", "references": { "TranslationService": { "location": "import", "path": "../../../utils/translation.service" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Instance of TranslationService passed from root component." } }, "apiState": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Api state passed from root component." }, "attribute": "api-state", "reflect": false }, "cameraFlipped": { "type": "boolean", "mutable": true, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Camera horizontal state passed from root component.\n\nHorizontal camera image can be mirrored" }, "attribute": "camera-flipped", "reflect": false, "defaultValue": "false" }, "showScanningLine": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Show scanning line on camera" }, "attribute": "show-scanning-line", "reflect": false, "defaultValue": "false" }, "showCameraFeedbackBarcodeMessage": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Show camera feedback message on camera for Barcode scanning" }, "attribute": "show-camera-feedback-barcode-message", "reflect": false, "defaultValue": "false" }, "clearIsCameraActive": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "" }, "attribute": "clear-is-camera-active", "reflect": false, "defaultValue": "false" } }; } static get states() { return { "cameraCursorBarcodeClassName": {}, "cameraCursorIdentityCardClassName": {}, "cameraCursorPaymentCardClassName": {}, "scanningLineBarcodeClassName": {}, "scanningLinePaymentCardClassName": {}, "cameraMessageIdentityCardContent": {}, "cameraMessageIdentityCardClassName": {} }; } static get events() { return [{ "method": "close", "name": "close", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Emitted when user clicks on 'X' button." }, "complexType": { "original": "void", "resolved": "void", "references": {} } }, { "method": "setIsCameraActive", "name": "setIsCameraActive", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Emitted when camera stream becomes active." }, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} } }, { "method": "changeCameraDevice", "name": "changeCameraDevice", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Emitted when user selects a different camera device." }, "complexType": { "original": "CameraEntry", "resolved": "CameraEntry", "references": { "CameraEntry": { "location": "import", "path": "../../../utils/data-structures" } } } }, { "method": "flipCameraAction", "name": "flipCameraAction", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Emitted when user clicks on Flip button." }, "complexType": { "original": "void", "resolved": "void", "references": {} } }]; } static get methods() { return { "setActiveCamera": { "complexType": { "signature": "(cameraId: string) => Promise<void>", "parameters": [{ "tags": [], "text": "" }], "references": { "Promise": { "location": "global" } }, "return": "Promise<void>" }, "docs": { "text": "Change active camera.", "tags": [] } }, "populateCameraDevices": { "complexType": { "signature": "() => Promise<void>", "parameters": [], "references": { "Promise": { "location": "global" } }, "return": "Promise<void>" }, "docs": { "text": "Populate list of camera devices.", "tags": [] } }, "setCameraFlipState": { "complexType": { "signature": "(isFlipped: boolean) => Promise<void>", "parameters": [{ "tags": [], "text": "" }], "references": { "Promise": { "location": "global" } }, "return": "Promise<void>" }, "docs": { "text": "Method is exposed outside which allow us to control Camera Flip state from parent component.", "tags": [] } }, "setState": { "complexType": { "signature": "(state: CameraExperienceState, isBackSide?: boolean, force?: boolean) => Promise<void>", "parameters": [{ "tags": [], "text": "" }, { "tags": [], "text": "" }, { "tags": [], "text": "" }], "references": { "Promise": { "location": "global" }, "CameraExperienceState": { "location": "import", "path": "../../../utils/data-structures" } }, "return": "Promise<void>" }, "docs": { "text": "Set camera state which includes animation and message.", "tags": [] } }, "resetState": { "complexType": { "signature": "() => Promise<void>", "parameters": [], "references": { "Promise": { "location": "global" } }, "return": "Promise<void>" }, "docs": { "text": "Set camera state to initial method.", "tags": [] } } }; } static get elementRef() { return "hostEl"; } static get watchers() { return [{ "propName": "apiState", "methodName": "apiStateHandler" }]; } }