diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e370a7f --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +dist/* +!dist/easy-barcode-scanner.js +node_modules +.npmrc +package-lock.json \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fdddb29 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/README.md b/README.md index 76b9cbc..47736f3 100644 --- a/README.md +++ b/README.md @@ -1 +1,153 @@ -# easy-barcode-scanner \ No newline at end of file +# Easy Barcode Scanner + +The Easy Barcode Scanner is a lightweight, user-friendly wrapper for the Dynamsoft Barcode Reader SDK. It simplifies the barcode scanning process, making it easier to integrate into your web applications with minimal effort. + +**Features** +* Supports video-based barcode scanning +* Handles multiple barcodes with ease +* Simple integration with just a few lines of code + +## Out-of-the-box Scanning + +The simplest way to use Easy Barcode Scanner requires only one line code to create a video decoding web application. + +```html + + + +``` +[Source Code >>](https://github.com/Dynamsoft/easy-barcode-scanner/blob/main/index.html) | [Run in github.io >>](https://Dynamsoft.github.io/easy-barcode-scanner/index.html) + +![Out-of-the-box Scanning](./out-off-box-scan.png) + +## Create Your Own Scanner for Further Control + +You can also create your own scanner instance to have more control over the entire workflow. For more details on the encapsulated functionality, refer to `src/index.ts`, and feel free to modify it based on your specific needs. + +```html +
+ + + + +``` + +## How to use it in frameworks + +To integrate Easy Barcode Scanner into your framework, follow these steps: + +1. Install the necessary package: + +```sh +npm i dynamsoft-barcode-reader-bundle@10.2.1000 -E +``` + +2. Copy the `src/index.ts` file from the library into your project. Rename it as needed, for example: `[your-path]/easy-barcode-reader.ts.` + +**Example 1: Simple Out-of-the-box Scan** +For a simpler implementation, this example shows how to scan with a single function: + +```ts +import EasyBarcodeScanner from '[your-path]/easy-barcode-reader'; + +EasyBarcodeScanner.license = ""; // Add your license key here + +async scan(){ + try{ + alert(await EasyBarcodeScanner.scan()); + }catch(ex){ + let errMsg = ex.message || ex; + console.error(errMsg); + alert(errMsg); + } +} +``` + +**Example 2: Setting Up a Scanner** +This example demonstrates how to create a scanner instance and handle barcode readings efficiently: + +```tsx +import EasyBarcodeScanner from '[your-path]/easy-barcode-reader'; + +EasyBarcodeScanner.license = ""; // Add your license key here + +let pScanner = null; +let scanner = null; + +async mount(){ + try{ + scanner = await (pScanner || (pScanner = EasyBarcodeScanner.createInstance())); + cameraViewContainer.append(scanner.getUIElement()); // Optional. + scanner.onUniqueRead = (txt) => { console.log(txt); }; + await scanner.open(); + }catch(ex){ + let errMsg = ex.message || ex; + console.error(errMsg); + alert(errMsg); + } +} +beforeUnmount(){ + // Clean up to free resources + try{ (await pScanner)?.dispose(); }catch(_){} +} + +// usage example in a tsx/jsx component +
+``` + +* The `mount()` function initializes the scanner and listens for barcode readings. +* The `beforeUnmount()` function disposes of the scanner instance to prevent memory leaks. + +## Customize the UI + +The built-in UIs are located in files like `xxx.ui.html`. You can copy `xxx.ui.html` into your project, modify it as needed, and pass its path to the `createInstance` or `scan` API to use the customized version. + +```typescript +// 'https://cdn.jsdelivr.net/gh/Dynamsoft/easy-barcode-scanner@10.2.1007/easy-barcode-scanner.ui.html' by default +EasyBarcodeScanner.scan(ui?: string|HTMLElement); +// 'https://cdn.jsdelivr.net/npm/dynamsoft-camera-enhancer@4.0.3/dist/dce.ui.html' by default +EasyBarcodeScanner.createInstance(ui?: string|HTMLElement); +``` + +Please refer to [customize the UI of CameraEnhancer](https://www.dynamsoft.com/camera-enhancer/docs/web/programming/javascript/user-guide/index.html#customize-the-ui) for more details. + +## All supported barcodes + +You can use the code snippet from the[Out-of-the-box Scanning](#out-of-the-box-scanning) section to focus the camera on one or more barcodes. If only one barcode is detected, the result will be displayed immediately. If multiple codes are scanned, an additional interactive step allows you to choose the target. + +![default supported barcode](./default-supported-barcode.png) + +> Please note that some barcode types are not supported by default for performance concern. Please check [here](https://www.dynamsoft.com/barcode-reader/docs/web/programming/javascript/user-guide/index.html#customize-the-process) to change settings. + +## License Information + +The license used in this sample is an automatically requested trial license, only valid for 24 hours and applicable to any newly authorized browser. To test the SDK further, you can request a 30-day free trial license via the Request a Trial License link. + +The license can be directly configured within the script tag when including the script file. + +```html + +``` diff --git a/default-supported-barcode.png b/default-supported-barcode.png new file mode 100644 index 0000000..f680ee9 Binary files /dev/null and b/default-supported-barcode.png differ diff --git a/dist/easy-barcode-scanner.js b/dist/easy-barcode-scanner.js new file mode 100644 index 0000000..efa6a19 --- /dev/null +++ b/dist/easy-barcode-scanner.js @@ -0,0 +1,7 @@ +/*! +* easy-barcode-scanner +* @version 10.2.1007 (build 2024-09-19T08:05:08.092Z) +* A wrapper for https://github.com/Dynamsoft/barcode-reader-javascript. Easier to use. +* The wrapper is under Unlicense, the Dynamsoft SDK it depended is still protected by copyright. +*/ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("dynamsoft-core"),require("dynamsoft-license"),require("dynamsoft-capture-vision-router"),require("dynamsoft-camera-enhancer"),require("dynamsoft-utility"),require("dynamsoft-barcode-reader")):"function"==typeof define&&define.amd?define(["dynamsoft-core","dynamsoft-license","dynamsoft-capture-vision-router","dynamsoft-camera-enhancer","dynamsoft-utility","dynamsoft-barcode-reader"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).EasyBarcodeScanner=t(e.Dynamsoft.Core,e.Dynamsoft.License,e.Dynamsoft.CVR,e.Dynamsoft.DCE,e.Dynamsoft.Utility)}(this,(function(e,t,n,a,s){"use strict";if(Object.assign(e.CoreModule.engineResourcePaths,{std:"https://cdn.jsdelivr.net/npm/dynamsoft-capture-vision-std@1.2.10/dist/",dip:"https://cdn.jsdelivr.net/npm/dynamsoft-image-processing@2.2.30/dist/",core:"https://cdn.jsdelivr.net/npm/dynamsoft-core@3.2.30/dist/",license:"https://cdn.jsdelivr.net/npm/dynamsoft-license@3.2.21/dist/",cvr:"https://cdn.jsdelivr.net/npm/dynamsoft-capture-vision-router@2.2.30/dist/",dbr:"https://cdn.jsdelivr.net/npm/dynamsoft-barcode-reader@10.2.10/dist/",dce:"https://cdn.jsdelivr.net/npm/dynamsoft-camera-enhancer@4.0.3/dist/"}),null!=typeof document){let e=null===document||void 0===document?void 0:document.currentScript;if(e){let n=e.getAttribute("data-license");n&&t.LicenseManager.license!=n&&(t.LicenseManager.license=n)}}class i{constructor(){this.templateName="ReadBarcodes_SpeedFirst",this.isBeepOnUniqueRead=!0}static get license(){return t.LicenseManager.license}static set license(e){t.LicenseManager.license!=e&&(t.LicenseManager.license=e)}get videoFit(){return this._view.getVideoFit()}set videoFit(e){this._view.setVideoFit(e)}get scanRegionMaskVisible(){return this._view.isScanRegionMaskVisible()}set scanRegionMaskVisible(e){this._view.setScanRegionMaskVisible(e)}get decodedBarcodeVisible(){return this._view._drawingLayerManager.getDrawingLayer(2).isVisible()}set decodedBarcodeVisible(e){this._view._drawingLayerManager.getDrawingLayer(2).setVisible(e)}get minImageCaptureInterval(){return this._cvRouter._minImageCaptureInterval}set minImageCaptureInterval(e){this._cvRouter._minImageCaptureInterval=e}static async createInstance(t){let r=new i;try{let i=r._cvRouter=await n.CaptureVisionRouter.createInstance(),o=r._view=await a.CameraView.createInstance(t),c=r._cameraEnhancer=await a.CameraEnhancer.createInstance(o);i.setInput(c);let d=r._filter=new s.MultiFrameResultCrossFilter;d.enableResultCrossVerification(e.EnumCapturedResultItemType.CRIT_BARCODE,!0),i.addResultFilter(d),i.addResultReceiver({onCapturedResultReceived:e=>{let t=e.barcodeResultItems;try{r.onFrameRead&&r.onFrameRead(t)}catch(e){}let n=!1;for(let e of t)if(!e.duplicate){n=!0;try{r.onUniqueRead&&r.onUniqueRead(e.text,e)}catch(e){}}n&&r.isBeepOnUniqueRead&&a.Feedback.beep()}})}catch(e){throw r.dispose(),e}return r}getUIElement(){return this._view.getUIElement()}setScanRegion(e){this._cameraEnhancer.setScanRegion(e)}getScanRegionMaskStyle(){return this._view.getScanRegionMaskStyle()}setScanRegionMaskStyle(e){this._view.setScanRegionMaskStyle(e)}getDecodedBarcodeStyle(){return a.DrawingStyleManager.getDrawingStyle(3)}setDecodedBarocdeStyle(e){a.DrawingStyleManager.updateDrawingStyle(3,e)}getOriginalCanvas(){return this._cvRouter._dsImage.toCanvas()}async open(){if(this._cameraEnhancer.isOpen())this._cameraEnhancer.isPaused()&&(await this._cameraEnhancer.resume(),await this._cvRouter.startCapturing(this.templateName));else{let e=this._view.getUIElement();e.parentElement||(Object.assign(e.style,{position:"fixed",left:"0",top:"0",width:"100vw",height:"100vh"}),document.body.append(e),this._bAddToBodyWhenOpen=!0),await this._cameraEnhancer.open(),await this._cvRouter.startCapturing(this.templateName)}}close(){let e=this._view.getUIElement();this._bAddToBodyWhenOpen&&(this._bAddToBodyWhenOpen=!1,document.body.removeChild(e)),this._cvRouter.stopCapturing(),this._cameraEnhancer.close()}pause(){this._cvRouter.stopCapturing(),this._cameraEnhancer.pause()}turnOnTorch(){this._cameraEnhancer.turnOnTorch()}turnOffTorch(){this._cameraEnhancer.turnOffTorch()}convertToPageCoordinates(e){return this._cameraEnhancer.convertToPageCoordinates(e)}convertToClientCoordinates(e){return this._cameraEnhancer.convertToClientCoordinates(e)}dispose(){var e,t,n;null===(e=this._cvRouter)||void 0===e||e.dispose();let a=null===(t=this._view)||void 0===t?void 0:t.getUIElement();null===(n=this._cameraEnhancer)||void 0===n||n.dispose(),this._bAddToBodyWhenOpen&&(this._bAddToBodyWhenOpen=!1,a&&document.body.removeChild(a))}static async scan(e="https://cdn.jsdelivr.net/gh/Dynamsoft/easy-barcode-scanner@10.2.1007/easy-barcode-scanner.ui.html"){return await new Promise((async(t,n)=>{let a=await i.createInstance(e),s=new Promise((e=>{a.onFrameRead=t=>{("disabled"!==a._cameraEnhancer.singleFrameMode||t.length)&&e(t)}})),r=a.getUIElement().shadowRoot,o=r.querySelector(".easyscanner-close-btn");o.addEventListener("click",(()=>{a.dispose(),t(null)})),r.querySelector(".easyscanner-photo-album-btn").addEventListener("click",(async()=>{a.close(),a._cameraEnhancer.singleFrameMode="image",await a.open()}));let c=r.querySelector(".easyscanner-camera-and-resolution-btn");c.addEventListener("pointerdown",(async()=>{"720P"===c.textContent?(a._cameraEnhancer.setResolution({width:1920,height:1080}),c.textContent="1080P"):(a._cameraEnhancer.setResolution({width:1280,height:720}),c.textContent="720P")}));let d=!1;r.querySelector(".easyscanner-flash-btn").addEventListener("pointerdown",(()=>{d=!d,d?a.turnOnTorch():a.turnOffTorch()})),await a.open();let l=await s;if(0===l.length)return void t(null);if(1===l.length)return a.dispose(),void t(l[0].text);let u=document.createElement("div");u.className="easyscanner-barcode-result-select-mask",r.append(u);let m=new Promise((e=>{for(let t of l){let n=0,s=0;for(let e=0;e<4;++e){let a=t.location.points[e];n+=a.x,s+=a.y}let i=a.convertToClientCoordinates({x:n/4,y:s/4}),o=document.createElement("div");o.className="easyscanner-barcode-result-option",o.style.left=i.x+"px",o.style.top=i.y+"px",o.addEventListener("click",(()=>{e(t.text)})),r.append(o)}})),h=document.createElement("div");h.className="easyscanner-barcode-result-select-tip",h.textContent="Multiple scans found, please select one.",r.append(h),r.append(o),"disabled"===a._cameraEnhancer.singleFrameMode&&a.pause();let p=await m;a.dispose(),t(p)}))}}return i})); diff --git a/easy-barcode-scanner.ui.html b/easy-barcode-scanner.ui.html new file mode 100644 index 0000000..f1233fc --- /dev/null +++ b/easy-barcode-scanner.ui.html @@ -0,0 +1,155 @@ + diff --git a/index.html b/index.html new file mode 100644 index 0000000..d1a31e7 --- /dev/null +++ b/index.html @@ -0,0 +1,26 @@ + + + + + + Document + + + + + + + + \ No newline at end of file diff --git a/out-off-box-scan.png b/out-off-box-scan.png new file mode 100644 index 0000000..344f0d2 Binary files /dev/null and b/out-off-box-scan.png differ diff --git a/package.json b/package.json new file mode 100644 index 0000000..3e9da73 --- /dev/null +++ b/package.json @@ -0,0 +1,57 @@ +{ + "name": "easy-barcode-scanner", + "version": "10.2.1007", + "description": "A wrapper for dynamsoft-barcode-reader-javascript. Easier to use.", + "main": "./dist/easy-barcode-scanner.js", + "module": "./dist/easy-barcode-scanner.esm.js", + "files": [ + "/dist", + "/easy-barcode-scanner.ui.html", + "/LICENSE" + ], + "sideEffects": false, + "exports": { + ".": { + "types": { + "require": "./dist/types/index.d.cts", + "import": "./dist/types/index.d.ts" + }, + "import": "./dist/easy-barcode-scanner.esm.js", + "require": "./dist/easy-barcode-scanner.js" + } + }, + "types": "./dist/types/index.d.ts", + "type": "module", + "scripts": { + "build": "rollup -c", + "build:production": "rollup -c --environment BUILD:production", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Dynamsoft/easy-barcode-scanner.git" + }, + "keywords": [ + "barcode", + "js", + "javascript" + ], + "author": "Dynamsoft", + "license": "Unlicense", + "bugs": { + "url": "https://github.com/Dynamsoft/easy-barcode-scanner/issues" + }, + "homepage": "https://www.dynamsoft.com", + "devDependencies": { + "@rollup/plugin-node-resolve": "^15.2.3", + "@rollup/plugin-terser": "^0.4.4", + "@rollup/plugin-typescript": "^11.1.6", + "@types/node": "^20.11.6", + "rollup": "^4.9.5", + "tslib": "^2.6.2", + "typescript": "^5.3.3" + }, + "dependencies": { + "dynamsoft-barcode-reader-bundle": "10.2.1000" + } +} diff --git a/rollup.config.mjs b/rollup.config.mjs new file mode 100644 index 0000000..e238519 --- /dev/null +++ b/rollup.config.mjs @@ -0,0 +1,74 @@ +import fs from 'fs'; +import typescript from "@rollup/plugin-typescript"; +import { nodeResolve } from "@rollup/plugin-node-resolve"; +import terser from "@rollup/plugin-terser"; + +import pkg from "./package.json" assert { type: "json" }; +const version = pkg.version; + +const hasSourceMap = 'production' !== process.env.BUILD; + +const banner = `/*! +* easy-barcode-scanner +* @version ${version} (build ${(new Date()).toISOString()}) +* A wrapper for https://github.com/Dynamsoft/barcode-reader-javascript. Easier to use. +* The wrapper is under Unlicense, the Dynamsoft SDK it depended is still protected by copyright. +*/`; +export default async (commandLineArgs)=>{ + fs.rmSync('dist', {recursive: true, force: true }); + + + return [ + { + input: "src/index.ts", + plugins: [ + nodeResolve(), + typescript({ tsconfig: "./tsconfig.json", sourceMap: hasSourceMap }), + ], + external: [ + "dynamsoft-core", + "dynamsoft-license", + "dynamsoft-capture-vision-router", + "dynamsoft-camera-enhancer", + "dynamsoft-barcode-reader", + "dynamsoft-utility", + ], + output: [ + { + file: "dist/easy-barcode-scanner.js", + format: "umd", + name: "EasyBarcodeScanner", + exports: "default", + banner: banner, + sourcemap: hasSourceMap, + globals: { + "dynamsoft-core": "Dynamsoft.Core", + "dynamsoft-license": "Dynamsoft.License", + "dynamsoft-capture-vision-router": "Dynamsoft.CVR", + "dynamsoft-camera-enhancer": "Dynamsoft.DCE", + "dynamsoft-barcode-reader": "Dynamsoft.DBR", + "dynamsoft-utility": "Dynamsoft.Utility", + }, + plugins: [terser({ ecma: 5 })], + }, + { + file: "dist/easy-barcode-scanner.mjs", + format: "es", + exports: "default", + banner: banner, + sourcemap: hasSourceMap, + plugins: [ + terser({ ecma: 6 }), + { + // https://rollupjs.org/guide/en/#writebundle + writeBundle(options, bundle){ + fs.cpSync('dist/easy-barcode-scanner.mjs','dist/easy-barcode-scanner.esm.js'); + } + }, + ], + }, + ], + }, + ] +}; + diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..ffb2ff6 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,304 @@ +import { CoreModule, EnumCapturedResultItemType, Rect, DSRect, Point } from 'dynamsoft-core'; +import { LicenseManager } from 'dynamsoft-license'; +import { CaptureVisionRouter } from 'dynamsoft-capture-vision-router'; +import { CameraEnhancer, CameraView, Feedback, DrawingStyleManager, DrawingStyle } from 'dynamsoft-camera-enhancer'; +import { BarcodeResultItem } from 'dynamsoft-barcode-reader'; +import { MultiFrameResultCrossFilter } from 'dynamsoft-utility'; +import 'dynamsoft-barcode-reader'; + + +//The following code uses the jsDelivr CDN, feel free to change it to your own location of these files +Object.assign(CoreModule.engineResourcePaths, { + std: "https://cdn.jsdelivr.net/npm/dynamsoft-capture-vision-std@1.2.10/dist/", + dip: "https://cdn.jsdelivr.net/npm/dynamsoft-image-processing@2.2.30/dist/", + core: "https://cdn.jsdelivr.net/npm/dynamsoft-core@3.2.30/dist/", + license: "https://cdn.jsdelivr.net/npm/dynamsoft-license@3.2.21/dist/", + cvr: "https://cdn.jsdelivr.net/npm/dynamsoft-capture-vision-router@2.2.30/dist/", + dbr: "https://cdn.jsdelivr.net/npm/dynamsoft-barcode-reader@10.2.10/dist/", + dce: "https://cdn.jsdelivr.net/npm/dynamsoft-camera-enhancer@4.0.3/dist/" +}); + +if(typeof document != undefined){ + let cs = document?.currentScript; + if(cs){ + let license = cs.getAttribute('data-license'); + if(license && LicenseManager.license != license){ LicenseManager.license = license; } + } +} + +class EasyBarcodeScanner{ + // static initLicense = LicenseManager.initLicense.bind(this) as typeof LicenseManager.initLicense; + + static get license(){ return LicenseManager.license; } + static set license(value: string){ if(LicenseManager.license != value){ LicenseManager.license = value; } } + + /** + * Presets: "ReadSingleBarcode", "ReadBarcodes_SpeedFirst" + */ + templateName = "ReadBarcodes_SpeedFirst"; + isBeepOnUniqueRead = true; + + _cvRouter: CaptureVisionRouter; + _view: CameraView; + _cameraEnhancer: CameraEnhancer; + _filter: MultiFrameResultCrossFilter; + + _bAddToBodyWhenOpen:boolean; + + get videoFit(){ return this._view.getVideoFit(); } + set videoFit(value: 'contain'|'cover'){ this._view.setVideoFit(value); } + // get singleFrameMode(){ return this._cameraEnhancer.singleFrameMode; } + // set singleFrameMode(value: "disabled" | "camera" | "image"){ this._cameraEnhancer.singleFrameMode = value; } + + get scanRegionMaskVisible(){ return this._view.isScanRegionMaskVisible(); } + set scanRegionMaskVisible(value: boolean){ this._view.setScanRegionMaskVisible(value); } + get decodedBarcodeVisible(){ return this._view._drawingLayerManager.getDrawingLayer(2).isVisible(); } + set decodedBarcodeVisible(value: boolean){ this._view._drawingLayerManager.getDrawingLayer(2).setVisible(value); } + get minImageCaptureInterval(){ return (this._cvRouter as any)._minImageCaptureInterval; } + set minImageCaptureInterval(value: number){ (this._cvRouter as any)._minImageCaptureInterval = value; } + + onFrameRead:(results:BarcodeResultItem[])=>void|any; + onUniqueRead:(txt:string, result:BarcodeResultItem)=>void|any; + + + static createInstance(): Promise; + static createInstance(uiPath: string): Promise; + static createInstance(uiElement: HTMLElement): Promise; + static createInstance(ui?: string | HTMLElement): Promise; + static async createInstance(ui?: string | HTMLElement){ + let scanner = new EasyBarcodeScanner(); + try{ + let cvRouter = scanner._cvRouter = await CaptureVisionRouter.createInstance(); + + // let settings = await cvRouter.getSimplifiedSettings('ReadBarcodes_SpeedFirst'); + // settings.capturedResultItemTypes = EnumCapturedResultItemType.CRIT_BARCODE; + // await cvRouter.updateSettings('ReadBarcodes_SpeedFirst', settings); + + let view = scanner._view = await CameraView.createInstance(ui); + let cameraEnhancer = scanner._cameraEnhancer = await CameraEnhancer.createInstance(view); + cvRouter.setInput(cameraEnhancer); + + let filter = scanner._filter = new MultiFrameResultCrossFilter(); + filter.enableResultCrossVerification(EnumCapturedResultItemType.CRIT_BARCODE, true); + //filter.enableResultDeduplication(EnumCapturedResultItemType.CRIT_BARCODE, true); + cvRouter.addResultFilter(filter); + + cvRouter.addResultReceiver({ + onCapturedResultReceived: (results)=>{ + let items = results.barcodeResultItems; + + try{scanner.onFrameRead && scanner.onFrameRead(items)}catch(_){} + + let hasUnique = false; + for(let item of items){ + if(!(item as any).duplicate){ + hasUnique = true; + try{scanner.onUniqueRead && scanner.onUniqueRead(item.text, item)}catch(_){} + } + } + if(hasUnique&& scanner.isBeepOnUniqueRead){ Feedback.beep(); } + } + }); + }catch(ex){ + scanner.dispose(); + throw ex; + } + + return scanner; + } + + getUIElement(){ return this._view.getUIElement(); } + + setScanRegion(region?: Rect | DSRect){ + this._cameraEnhancer.setScanRegion(region); + } + + getScanRegionMaskStyle(){ + return this._view.getScanRegionMaskStyle(); + } + setScanRegionMaskStyle(style: {lineWidth:number,strokeStyle:string,fillStyle:string}){ + this._view.setScanRegionMaskStyle(style); + } + getDecodedBarcodeStyle(){ + return DrawingStyleManager.getDrawingStyle(3); + } + setDecodedBarocdeStyle(value: DrawingStyle){ + DrawingStyleManager.updateDrawingStyle(3, value); + } + + getOriginalCanvas(){ return (this._cvRouter as any)._dsImage.toCanvas(); } + + async open(){ + if(!this._cameraEnhancer.isOpen()){ + let ui = this._view.getUIElement(); + if(!ui.parentElement){ + Object.assign(ui.style, { + position: 'fixed', + left: '0', + top: '0', + width: '100vw', + height: '100vh', + }); + document.body.append(ui); + this._bAddToBodyWhenOpen = true; + } + await this._cameraEnhancer.open(); + await this._cvRouter.startCapturing(this.templateName); + }else if(this._cameraEnhancer.isPaused()){ + await this._cameraEnhancer.resume(); + await this._cvRouter.startCapturing(this.templateName); + } + } + close(){ + let ui = this._view.getUIElement(); + if(this._bAddToBodyWhenOpen){ + this._bAddToBodyWhenOpen = false; + document.body.removeChild(ui); + } + this._cvRouter.stopCapturing(); + this._cameraEnhancer.close(); + } + pause(){ + this._cvRouter.stopCapturing(); + this._cameraEnhancer.pause(); + } + + turnOnTorch(){ this._cameraEnhancer.turnOnTorch(); } + turnOffTorch(){ this._cameraEnhancer.turnOffTorch(); } + //turnAutoTorch(){ this._cameraEnhancer.turnAutoTorch(); } + + convertToPageCoordinates(point: Point){ return this._cameraEnhancer.convertToPageCoordinates(point); } + convertToClientCoordinates(point: Point){ return this._cameraEnhancer.convertToClientCoordinates(point); } + + dispose(){ + this._cvRouter?.dispose(); + let ui = this._view?.getUIElement(); + this._cameraEnhancer?.dispose(); + if(this._bAddToBodyWhenOpen){ + this._bAddToBodyWhenOpen = false; + ui && document.body.removeChild(ui); + } + } + + static scan(): Promise; + static scan(uiPath: string): Promise; + static scan(uiElement: HTMLElement): Promise; + static scan(ui?: string | HTMLElement): Promise; + static async scan(ui: string | HTMLElement = 'https://cdn.jsdelivr.net/gh/Dynamsoft/easy-barcode-scanner@10.2.1007/easy-barcode-scanner.ui.html'){ + return await new Promise(async(rs,rj)=>{ + + //========================== init ============================ + + let scanner = await EasyBarcodeScanner.createInstance(ui); + + //========================== receive result ============================ + let pVideoScan = new Promise(rs=>{ + // let iRound = 0; + scanner.onFrameRead = barcodeResults=>{ + if('disabled' !== scanner._cameraEnhancer.singleFrameMode){ + rs(barcodeResults); + }else if(barcodeResults.length){ + //// 1D barcode need 2 frame to comfirm + //// so if you need choose between 1D and QR in one frame, you need `iRound` + // if(2 == ++iRound){ rs(barcodeResults); } + //// we don't use `iRound` for faster result + rs(barcodeResults); + } + }; + }); + + //========================== ui and event ============================ + let shadowRoot = scanner.getUIElement().shadowRoot; + + let btnClose = shadowRoot.querySelector('.easyscanner-close-btn'); + btnClose.addEventListener('click',()=>{ + scanner.dispose(); + rs(null); + }); + + shadowRoot.querySelector('.easyscanner-photo-album-btn').addEventListener('click',async()=>{ + scanner.close(); + scanner._cameraEnhancer.singleFrameMode = 'image'; + await scanner.open(); + }); + + let btnResolution = shadowRoot.querySelector('.easyscanner-camera-and-resolution-btn'); + btnResolution.addEventListener('pointerdown',async()=>{ + if('720P' === btnResolution.textContent){ + scanner._cameraEnhancer.setResolution({ width: 1920, height: 1080 }); + btnResolution.textContent = '1080P'; + }else{ + scanner._cameraEnhancer.setResolution({ width: 1280, height: 720 }); + btnResolution.textContent = '720P'; + } + }); + + let isTorchOn = false; // torch has another style, you can check html part + shadowRoot.querySelector('.easyscanner-flash-btn').addEventListener('pointerdown', ()=>{ + isTorchOn = !isTorchOn; + isTorchOn ? scanner.turnOnTorch() : scanner.turnOffTorch(); + }); + + // easyscanner-more-settings-btn not used + + await scanner.open(); + + let barcodeResults = await pVideoScan; + //========================== success get result ============================ + + if(0 === barcodeResults.length){ + rs(null); + return; + } + + if(1 === barcodeResults.length){ + scanner.dispose(); + rs(barcodeResults[0].text); + return; + } + + + let mask = document.createElement('div'); + mask.className = 'easyscanner-barcode-result-select-mask'; + shadowRoot.append(mask); + + let pChooseResult = new Promise(rs=>{ + for(let barcodeResult of barcodeResults){ + let xSum = 0, ySum = 0; + for(let i = 0; i < 4; ++i){ + let p = barcodeResult.location.points[i]; + xSum += p.x; + ySum += p.y; + } + let center = scanner.convertToClientCoordinates({x: xSum/4, y: ySum/4}); + let option = document.createElement('div'); + option.className = 'easyscanner-barcode-result-option'; + option.style.left = center.x + 'px'; + option.style.top = center.y + 'px'; + option.addEventListener('click',()=>{ + rs(barcodeResult.text); + }); + shadowRoot.append(option); + } + }); + + let tip = document.createElement('div'); + tip.className = 'easyscanner-barcode-result-select-tip'; + tip.textContent = 'Multiple scans found, please select one.'; + shadowRoot.append(tip); + + shadowRoot.append(btnClose); + + if('disabled' === scanner._cameraEnhancer.singleFrameMode){ scanner.pause(); } + + let txtResult = await pChooseResult; + + scanner.dispose(); + + rs(txtResult); + }); + } +} + +export default EasyBarcodeScanner; + diff --git a/test.html b/test.html new file mode 100644 index 0000000..1135a2b --- /dev/null +++ b/test.html @@ -0,0 +1,28 @@ + + + + + Document + + + +
+
+ + + + + + \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..d8af0b5 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "es2017", + "module": "esnext", + //"lib": ["DOM","ES2015"], + //"strict": true, + "outDir": "dist", + "moduleResolution": "node", + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "sourceMap": true, + "declaration": true, + "declarationMap": true, + "declarationDir": "./types", + "noImplicitAny": true, + "resolveJsonModule": true, + // "types": ["node"], + // "removeComments": true + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "node_modules/**" + ], +} \ No newline at end of file