Compare commits
34 Commits
Author | SHA1 | Date |
---|---|---|
![]() |
cfd924beab | 1 year ago |
![]() |
8fb5b98ea2 | 1 year ago |
![]() |
9fa47676df | 3 years ago |
![]() |
0b7edb39df | 3 years ago |
![]() |
8afbcdad33 | 3 years ago |
![]() |
0f2e84268c | 3 years ago |
![]() |
33f115036f | 3 years ago |
![]() |
17ced673e4 | 3 years ago |
![]() |
c236e43f21 | 3 years ago |
![]() |
e6fb08352f | 3 years ago |
![]() |
b9d89e0545 | 3 years ago |
![]() |
aeee33ee6c | 3 years ago |
![]() |
a5cd17b951 | 3 years ago |
![]() |
615081cc95 | 3 years ago |
![]() |
7d77236fc0 | 3 years ago |
![]() |
8c80e312b3 | 4 years ago |
![]() |
6ab5e80028 | 4 years ago |
![]() |
05b1c0d310 | 4 years ago |
![]() |
4161b77272 | 4 years ago |
![]() |
be1f6a980b | 4 years ago |
![]() |
44737ddfe7 | 4 years ago |
![]() |
032f9ba16a | 4 years ago |
![]() |
d1a5b9de6b | 4 years ago |
![]() |
fd6daa86a1 | 4 years ago |
![]() |
b8d25e8969 | 4 years ago |
![]() |
455b0deb25 | 4 years ago |
![]() |
d77b666da2 | 4 years ago |
![]() |
32c08b7599 | 4 years ago |
![]() |
900b451788 | 4 years ago |
![]() |
2b19762d21 | 4 years ago |
![]() |
d55e4ddcef | 5 years ago |
![]() |
2737d1970b | 5 years ago |
![]() |
dfccc2f09a | 6 years ago |
![]() |
0996ab57ad | 6 years ago |
@ -1,22 +1,71 @@
|
|||||||
{
|
{
|
||||||
"presets": [
|
"presets": [
|
||||||
["env",
|
[
|
||||||
{
|
"@babel/env",
|
||||||
"targets": {
|
{
|
||||||
"browsers": "last 2 versions, > 1%, ie >= 11",
|
"modules": false,
|
||||||
"node": "8.0"
|
"targets": {
|
||||||
},
|
"browsers": [
|
||||||
"modules": false,
|
">0.25%"
|
||||||
"loose": false
|
]
|
||||||
}],
|
},
|
||||||
"stage-2"
|
"useBuiltIns": "usage"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@babel/typescript"
|
||||||
],
|
],
|
||||||
|
"env": {
|
||||||
|
"development": {
|
||||||
|
"plugins": [
|
||||||
|
"@babel/transform-runtime"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"production": {
|
||||||
|
"plugins": [
|
||||||
|
"@babel/transform-runtime",
|
||||||
|
"transform-remove-console"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"presets": [
|
||||||
|
[
|
||||||
|
"@babel/env",
|
||||||
|
{
|
||||||
|
"modules": "commonjs",
|
||||||
|
"targets": {
|
||||||
|
"node": "current"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@babel/typescript"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
["transform-runtime", {
|
"@babel/plugin-syntax-dynamic-import",
|
||||||
"helpers": false,
|
"@babel/plugin-syntax-import-meta",
|
||||||
"polyfill": false,
|
"@babel/plugin-proposal-class-properties",
|
||||||
"regenerator": false,
|
"@babel/plugin-proposal-json-strings",
|
||||||
"moduleName": "babel-runtime"
|
[
|
||||||
}]
|
"@babel/plugin-proposal-decorators",
|
||||||
|
{
|
||||||
|
"legacy": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@babel/plugin-proposal-function-sent",
|
||||||
|
"@babel/plugin-proposal-export-namespace-from",
|
||||||
|
"@babel/plugin-proposal-numeric-separator",
|
||||||
|
"@babel/plugin-proposal-throw-expressions",
|
||||||
|
"@babel/plugin-proposal-export-default-from",
|
||||||
|
"@babel/plugin-proposal-logical-assignment-operators",
|
||||||
|
"@babel/plugin-proposal-optional-chaining",
|
||||||
|
[
|
||||||
|
"@babel/plugin-proposal-pipeline-operator",
|
||||||
|
{
|
||||||
|
"proposal": "minimal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@babel/plugin-proposal-nullish-coalescing-operator",
|
||||||
|
"@babel/plugin-proposal-do-expressions"
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -1,12 +1,7 @@
|
|||||||
root = true
|
root = true
|
||||||
|
[*]
|
||||||
[{*.js,*.css,*.html}]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 4
|
|
||||||
end_of_line = lf
|
|
||||||
charset = utf-8
|
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
[*.{js,ts}]
|
||||||
[{package.json,.*rc,*.yml}]
|
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
charset = utf-8
|
@ -1 +0,0 @@
|
|||||||
test
|
|
@ -1,45 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
|
|
||||||
parserOptions: {
|
|
||||||
sourceType: 'module',
|
|
||||||
},
|
|
||||||
|
|
||||||
env: {
|
|
||||||
browser: true,
|
|
||||||
es6: true,
|
|
||||||
node: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
extends: ['airbnb-base'],
|
|
||||||
|
|
||||||
rules: {
|
|
||||||
'no-shadow': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
allow: ['state'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
// 'import/extensions': 'off',
|
|
||||||
'import/extensions': [
|
|
||||||
'error',
|
|
||||||
'always',
|
|
||||||
{
|
|
||||||
js: 'never',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'import/no-unresolved': 'off',
|
|
||||||
'no-param-reassign': 'off',
|
|
||||||
'consistent-return': 'off',
|
|
||||||
'global-require': 'off',
|
|
||||||
'import/no-dynamic-require': 'off',
|
|
||||||
'import/no-extraneous-dependencies': 'off',
|
|
||||||
|
|
||||||
// 4 行空格缩进
|
|
||||||
indent: ['error', 4, { SwitchCase: 1 }],
|
|
||||||
|
|
||||||
'max-len': ['error', { code: 150 }],
|
|
||||||
|
|
||||||
'operator-linebreak': 0,
|
|
||||||
},
|
|
||||||
};
|
|
@ -0,0 +1,108 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
env:
|
||||||
|
NODE_OPTIONS: --max-old-space-size=6144
|
||||||
|
# install playwright binary manually (because pnpm only runs install script once)
|
||||||
|
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- master
|
||||||
|
- release/*
|
||||||
|
- feat/*
|
||||||
|
- fix/*
|
||||||
|
- perf/*
|
||||||
|
- v1
|
||||||
|
- v2
|
||||||
|
pull_request:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.number || github.sha }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
timeout-minutes: 20
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest]
|
||||||
|
node_version: [16]
|
||||||
|
fail-fast: false
|
||||||
|
|
||||||
|
name: "Build&Test: node-${{ matrix.node_version }}, ${{ matrix.os }}"
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v2.2.2
|
||||||
|
|
||||||
|
- name: Set node version to ${{ matrix.node_version }}
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node_version }}
|
||||||
|
cache: "pnpm"
|
||||||
|
|
||||||
|
- name: Install deps
|
||||||
|
run: pnpm install
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: pnpm run build
|
||||||
|
|
||||||
|
test:
|
||||||
|
timeout-minutes: 10
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: "Lint: node-16, ubuntu-latest"
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v2.2.2
|
||||||
|
|
||||||
|
- name: Set node version to 16
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 16
|
||||||
|
cache: "pnpm"
|
||||||
|
|
||||||
|
- name: Install deps
|
||||||
|
run: pnpm install
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: pnpm run build
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: pnpm run test
|
||||||
|
|
||||||
|
lint:
|
||||||
|
timeout-minutes: 10
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: "Lint: node-16, ubuntu-latest"
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v2.2.2
|
||||||
|
|
||||||
|
- name: Set node version to 16
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 16
|
||||||
|
cache: "pnpm"
|
||||||
|
|
||||||
|
- name: Install deps
|
||||||
|
run: pnpm install
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: pnpm run build
|
||||||
|
|
||||||
|
- name: Check formatting
|
||||||
|
run: pnpm run prettier
|
@ -1,3 +1,13 @@
|
|||||||
node_modules
|
node_modules
|
||||||
|
.DS_STORE
|
||||||
|
lerna-debug.log
|
||||||
|
yarn-error.log
|
||||||
dist
|
dist
|
||||||
.DS_Store
|
includes
|
||||||
|
tsconfig.tsbuildinfo
|
||||||
|
.env
|
||||||
|
coverage
|
||||||
|
yarn.lock
|
||||||
|
.rpt2_cache
|
||||||
|
|
||||||
|
demo/lib/index.min.js
|
||||||
|
@ -1,11 +1,29 @@
|
|||||||
node_modules
|
node_modules
|
||||||
config
|
*.map
|
||||||
demo
|
tests
|
||||||
src
|
|
||||||
test
|
test
|
||||||
.babelrc
|
__tests__
|
||||||
|
__test__
|
||||||
|
.github
|
||||||
|
.env*
|
||||||
|
jest.config.js
|
||||||
|
.eslintrc.js
|
||||||
|
.eslintignore
|
||||||
|
.prettierignore
|
||||||
|
prettier.config.js
|
||||||
|
release.config.js
|
||||||
|
commitlint.config.js
|
||||||
.editorconfig
|
.editorconfig
|
||||||
.travis.yml
|
lerna.json
|
||||||
CHANGELOG.md
|
tsconfig.tsbuildinfo
|
||||||
package-lock.json
|
tsconfig.json
|
||||||
TODO.md
|
tsconfig.base.json
|
||||||
|
.rpt2_cache
|
||||||
|
.vscode
|
||||||
|
/demo
|
||||||
|
/doc
|
||||||
|
/scripts
|
||||||
|
tslint.json
|
||||||
|
yarn-error.log
|
||||||
|
.babelrc
|
||||||
|
yarn.lock
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
shamefully-hoist=true
|
||||||
|
strict-peer-dependencies=true
|
||||||
|
auto-install-peers=true
|
@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
|
||||||
|
# Development folders and files #
|
||||||
|
#################################
|
||||||
|
.tmp/
|
||||||
|
node_modules/
|
||||||
|
package.json
|
||||||
|
.travis.yml
|
||||||
|
dist
|
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"printWidth": 80,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"arrowParens": "always",
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": "*.json",
|
||||||
|
"options": {
|
||||||
|
"parser": "json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -1,3 +0,0 @@
|
|||||||
language: node_js
|
|
||||||
node_js:
|
|
||||||
- "8"
|
|
@ -1 +0,0 @@
|
|||||||
# Change Log
|
|
@ -0,0 +1,26 @@
|
|||||||
|
import QrcodeDecoder from '../';
|
||||||
|
|
||||||
|
describe('QrcodeDecoder', () => {
|
||||||
|
test('use', async () => {
|
||||||
|
const res = await import('../dist');
|
||||||
|
|
||||||
|
expect(res.default).toEqual(QrcodeDecoder);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('new QrcodeDecoder()', async () => {
|
||||||
|
const qr = new QrcodeDecoder();
|
||||||
|
|
||||||
|
expect(qr).toBeDefined();
|
||||||
|
expect(qr.videoConstraints).toEqual({
|
||||||
|
video: {
|
||||||
|
width: { min: 360, ideal: 720, max: 1080 },
|
||||||
|
height: { min: 360, ideal: 720, max: 1080 },
|
||||||
|
facingMode: { exact: 'environment' },
|
||||||
|
},
|
||||||
|
audio: false,
|
||||||
|
});
|
||||||
|
expect(qr.decodeFromImage).toBeDefined();
|
||||||
|
expect(qr.decodeFromCamera).toBeDefined();
|
||||||
|
expect(qr.decodeFromVideo).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
@ -1,52 +0,0 @@
|
|||||||
// rollup.config.js
|
|
||||||
|
|
||||||
import babel from 'rollup-plugin-babel';
|
|
||||||
import nodeResolve from 'rollup-plugin-node-resolve';
|
|
||||||
import commonjs from 'rollup-plugin-commonjs';
|
|
||||||
import { uglify } from 'rollup-plugin-uglify';
|
|
||||||
import common from './rollup';
|
|
||||||
|
|
||||||
const prod = process.env.NODE_ENV;
|
|
||||||
|
|
||||||
export default {
|
|
||||||
input: 'src/index.js',
|
|
||||||
output: {
|
|
||||||
file: prod ? 'dist/index.aio.min.js' : 'dist/index.aio.js',
|
|
||||||
format: 'umd',
|
|
||||||
// 如果不同时使用 export 与 export default 可打开legacy
|
|
||||||
// legacy: true,
|
|
||||||
// name: common.name,
|
|
||||||
name: 'QrcodeDecoder',
|
|
||||||
banner: common.banner,
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
nodeResolve({
|
|
||||||
main: true,
|
|
||||||
}),
|
|
||||||
commonjs({
|
|
||||||
include: 'node_modules/**',
|
|
||||||
}),
|
|
||||||
babel({
|
|
||||||
runtimeHelpers: true,
|
|
||||||
exclude: 'node_modules/**',
|
|
||||||
}),
|
|
||||||
prod &&
|
|
||||||
uglify({
|
|
||||||
compress: {
|
|
||||||
drop_debugger: true,
|
|
||||||
drop_console: true,
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
comments: (node, comment) => {
|
|
||||||
if (comment.type === 'comment2') {
|
|
||||||
// multiline comment
|
|
||||||
return /@preserve|@license|@cc_on/i.test(
|
|
||||||
comment.value,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
};
|
|
@ -1,7 +0,0 @@
|
|||||||
import config from './rollup.config';
|
|
||||||
|
|
||||||
// ES output
|
|
||||||
config.output.format = 'es';
|
|
||||||
config.output.file = 'dist/index.esm.js';
|
|
||||||
|
|
||||||
export default config;
|
|
@ -1,21 +0,0 @@
|
|||||||
// rollup.config.js
|
|
||||||
|
|
||||||
import babel from 'rollup-plugin-babel';
|
|
||||||
import common from './rollup';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
input: 'src/index.js',
|
|
||||||
output: {
|
|
||||||
file: 'dist/index.js',
|
|
||||||
format: 'cjs',
|
|
||||||
// 如果不同时使用 export 与 export default 可打开legacy
|
|
||||||
// legacy: true,
|
|
||||||
banner: common.banner,
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
babel({
|
|
||||||
runtimeHelpers: true,
|
|
||||||
exclude: 'node_modules/**',
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
};
|
|
@ -1,18 +0,0 @@
|
|||||||
const pkg = require('../package.json');
|
|
||||||
|
|
||||||
// 兼容 qrcode-decoder 和 @yugasun/qrcode-decoder
|
|
||||||
const name = pkg.name.split('/').pop();
|
|
||||||
const { version } = pkg;
|
|
||||||
|
|
||||||
const banner = `/* @preserve
|
|
||||||
* qrcode-decoder ${version} (https://github.com/yugasun/qrcode-decoder)
|
|
||||||
* API https://github.com/yugasun/qrcode-decoder/blob/master/doc/api.md
|
|
||||||
* Copyright 2017-${new Date().getFullYear()} yugasun. All Rights Reserved
|
|
||||||
* Licensed under MIT (https://github.com/yugasun/qrcode-decoder/blob/master/LICENSE)
|
|
||||||
*/
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name,
|
|
||||||
banner,
|
|
||||||
};
|
|
@ -1,39 +1,54 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
|
||||||
<title>QrcodeDecoder - Camera</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<button id="start">Start</button> <button id="stop">Stop</button><br />
|
|
||||||
<span id="result">Click start to scan qrcode.</span><br />
|
|
||||||
<hr />
|
|
||||||
<video id="video" autoplay></video>
|
|
||||||
|
|
||||||
<script src="./index.aio.min.js"></script>
|
<head>
|
||||||
<script type="text/javascript">
|
<meta charset="UTF-8" />
|
||||||
function main() {
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
var qr = new QrcodeDecoder();
|
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||||
var video = document.querySelector('#video');
|
<title>QrcodeDecoder - Camera</title>
|
||||||
var start = document.querySelector('#start');
|
</head>
|
||||||
var stop = document.querySelector('#stop');
|
|
||||||
var result = document.querySelector('#result');
|
|
||||||
async function startScan() {
|
|
||||||
if (!qr.isCanvasSupported()) {
|
|
||||||
alert("Your browser doesn't match the required specs.");
|
|
||||||
throw new Error('Canvas and getUserMedia are required');
|
|
||||||
}
|
|
||||||
|
|
||||||
let code = await qr.decodeFromCamera(video);
|
<body>
|
||||||
console.log('code', code);
|
<button id="start">Start</button> <button id="stop">Stop</button><br />
|
||||||
result.innerText = "Result: " + code.data;
|
<span id="result">Click start to scan qrcode.</span><br />
|
||||||
}
|
<hr />
|
||||||
start.onclick = startScan;
|
<video id="video" autoplay></video>
|
||||||
|
|
||||||
|
<script src="./lib/vconsole.min.js"></script>
|
||||||
|
<script src="./lib/index.min.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var vConsole = new VConsole();
|
||||||
|
console.log('Hello world');
|
||||||
|
function main() {
|
||||||
|
var qr = new QrcodeDecoder.default();
|
||||||
|
var video = document.querySelector('#video');
|
||||||
|
var start = document.querySelector('#start');
|
||||||
|
var stop = document.querySelector('#stop');
|
||||||
|
var result = document.querySelector('#result');
|
||||||
|
async function startScan() {
|
||||||
|
if (!qr.isCanvasSupported()) {
|
||||||
|
alert("Your browser doesn't match the required specs.");
|
||||||
|
throw new Error('Canvas and getUserMedia are required');
|
||||||
|
}
|
||||||
|
|
||||||
|
let code = await qr.decodeFromCamera(video,
|
||||||
|
// you can customize your camera size like below
|
||||||
|
// {
|
||||||
|
// width: 400,
|
||||||
|
// height: 400,
|
||||||
|
// }
|
||||||
|
);
|
||||||
|
console.log('code', code);
|
||||||
|
result.innerText = 'Result: ' + code.data;
|
||||||
|
}
|
||||||
|
start.onclick = startScan;
|
||||||
|
|
||||||
|
stop.onclick = function () {
|
||||||
|
qr.stop();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
main();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
stop.onclick = function() {
|
|
||||||
qr.stop();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
main();
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,52 +1,66 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<title>QrcodeDecoder - Image</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<section>
|
|
||||||
<h3>Same domain image</h3>
|
|
||||||
<img src="./assets/qrcode.png" alt="qr code" /><br>
|
|
||||||
<button id="decode1">Decode!</button><br>
|
|
||||||
<span id="result1"></span><br>
|
|
||||||
</section>
|
|
||||||
<hr/>
|
|
||||||
<section>
|
|
||||||
<h3>Different domain image</h3>
|
|
||||||
<button id="decode2">Decode!</button><br>
|
|
||||||
<span id="result2"></span><br>
|
|
||||||
</section>
|
|
||||||
<script src="./index.aio.min.js"></script>
|
|
||||||
<script type="module">
|
|
||||||
function main() {
|
|
||||||
var qr = new QrcodeDecoder();
|
|
||||||
|
|
||||||
var btn1 = document.querySelector('button#decode1');
|
<head>
|
||||||
var btn2 = document.querySelector('button#decode2');
|
<meta charset="UTF-8" />
|
||||||
var result1 = document.querySelector('#result1');
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
var result2 = document.querySelector('#result2');
|
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||||
var img = document.querySelector('img');
|
<title>QrcodeDecoder - Image</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
btn1.onclick = async () => {
|
<body>
|
||||||
// you can also decode from image path
|
<section>
|
||||||
// const code = await qr.decodeFromImage('./assets/qrcode.png');
|
<h3>Same domain image</h3>
|
||||||
const code = await qr.decodeFromImage(img);
|
<img id="img1" src="./assets/qrcode.png" alt="qr code" /><br />
|
||||||
console.log(code);
|
<button id="decode1">Decode!</button><br />
|
||||||
result1.innerText = code.data;
|
<span id="result1"></span><br />
|
||||||
};
|
</section>
|
||||||
|
<hr />
|
||||||
|
<section>
|
||||||
|
<h3>Different domain image</h3>
|
||||||
|
<input
|
||||||
|
id="img2"
|
||||||
|
value="https://yugasun.com/static/wechat.jpg"
|
||||||
|
style="width: 400px" /><br />
|
||||||
|
<button id="decode2">Decode!</button><br />
|
||||||
|
<span id="result2"></span><br />
|
||||||
|
</section>
|
||||||
|
<script src="./lib/vconsole.min.js"></script>
|
||||||
|
<script src="./lib/index.min.js"></script>
|
||||||
|
<script type="module">
|
||||||
|
var vConsole = new VConsole();
|
||||||
|
function main() {
|
||||||
|
var qr = new QrcodeDecoder.default();
|
||||||
|
|
||||||
|
var btn1 = document.querySelector('button#decode1');
|
||||||
|
var btn2 = document.querySelector('button#decode2');
|
||||||
|
var result1 = document.querySelector('#result1');
|
||||||
|
var result2 = document.querySelector('#result2');
|
||||||
|
var img1 = document.querySelector('#img1');
|
||||||
|
var img2 = document.querySelector('#img2');
|
||||||
|
|
||||||
|
btn1.onclick = async () => {
|
||||||
|
// you can also decode from image path
|
||||||
|
// const code = await qr.decodeFromImage('./assets/qrcode.png');
|
||||||
|
const code = await qr.decodeFromImage(img1);
|
||||||
|
console.log(code);
|
||||||
|
result1.innerText = code.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
btn2.onclick = async () => {
|
||||||
|
// you can also decode from image path
|
||||||
|
// const code = await qr.decodeFromImage('./assets/qrcode.png');
|
||||||
|
const code = await qr.decodeFromImage(img2.value, {
|
||||||
|
crossOrigin: 'anonymous',
|
||||||
|
});
|
||||||
|
console.log(code);
|
||||||
|
result2.innerText = code.data;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
window.onload = () => {
|
||||||
|
main();
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
btn2.onclick = async () => {
|
|
||||||
// you can also decode from image path
|
|
||||||
// const code = await qr.decodeFromImage('./assets/qrcode.png');
|
|
||||||
const code = await qr.decodeFromImage('https://yugasun.com/static/wechat.jpg', {
|
|
||||||
crossOrigin: 'anonymous',
|
|
||||||
});
|
|
||||||
console.log(code);
|
|
||||||
result2.innerText = code.data;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
main();
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
</html>
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1,18 +1,18 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8" />
|
||||||
<title>QrcodeDecoder</title>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
</head>
|
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||||
<body>
|
<title>QrcodeDecoder</title>
|
||||||
|
</head>
|
||||||
<h1>QrcodeDecoder</h1>
|
<body>
|
||||||
<h2>Examples</h2>
|
<h1>QrcodeDecoder</h1>
|
||||||
<ul>
|
<h2>Examples</h2>
|
||||||
<li><a href="image.html">Image</a></li>
|
<ul>
|
||||||
<li><a href="video.html">Video</a></li>
|
<li><a href="image.html">Image</a></li>
|
||||||
<li><a href="camera.html">Camera</a></li>
|
<li><a href="video.html">Video</a></li>
|
||||||
</ul>
|
<li><a href="camera.html">Camera</a></li>
|
||||||
|
</ul>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1,39 +1,45 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<title>QrcodeDecoder - Video</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<button id="start">Start</button> <button id="stop">Stop</button><br />
|
|
||||||
<span id="result">Click start to scan qrcode.</span><br />
|
|
||||||
|
|
||||||
<video src="./assets/qrcode-video.mp4"></video>
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||||
|
<title>QrcodeDecoder - Video</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
<script src="./index.aio.min.js"></script>
|
<body>
|
||||||
<script type="text/javascript">
|
<button id="start">Start</button> <button id="stop">Stop</button><br />
|
||||||
function main() {
|
<span id="result">Click start to scan qrcode.</span><br />
|
||||||
var video = document.querySelector('video');
|
|
||||||
var result = document.querySelector('#result');
|
|
||||||
var start = document.querySelector('#start');
|
|
||||||
var stop = document.querySelector('#stop');
|
|
||||||
var qr = new QrcodeDecoder();
|
|
||||||
|
|
||||||
start.onclick = startScan;
|
<video src="./assets/qrcode-video.mp4"></video>
|
||||||
|
<script src="./lib/vconsole.min.js"></script>
|
||||||
|
<script src="./lib/index.min.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var vConsole = new VConsole();
|
||||||
|
function main() {
|
||||||
|
var video = document.querySelector('video');
|
||||||
|
var result = document.querySelector('#result');
|
||||||
|
var start = document.querySelector('#start');
|
||||||
|
var stop = document.querySelector('#stop');
|
||||||
|
var qr = new QrcodeDecoder.default();
|
||||||
|
|
||||||
stop.onclick = function() {
|
start.onclick = startScan;
|
||||||
qr.stop();
|
|
||||||
video.pause();
|
stop.onclick = function () {
|
||||||
};
|
qr.stop();
|
||||||
|
video.pause();
|
||||||
|
};
|
||||||
|
|
||||||
|
async function startScan() {
|
||||||
|
video.play();
|
||||||
|
const code = await qr.decodeFromVideo(video);
|
||||||
|
console.log('code', code);
|
||||||
|
result.innerText = 'Result: ' + code.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
async function startScan() {
|
|
||||||
video.play();
|
|
||||||
const code = await qr.decodeFromVideo(video);
|
|
||||||
console.log('code', code);
|
|
||||||
result.innerText = 'Result: ' + code.data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
main();
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
</html>
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
const isDebug = process.env.DEBUG === 'true';
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
verbose: true,
|
||||||
|
silent: !isDebug,
|
||||||
|
transform: {
|
||||||
|
'^.+\\.tsx?$': 'ts-jest',
|
||||||
|
},
|
||||||
|
testTimeout: 60000,
|
||||||
|
transform: {
|
||||||
|
'^.+\\.tsx?$': 'ts-jest',
|
||||||
|
},
|
||||||
|
testRegex: '(/__tests__/.*\\.(test|spec))\\.tsx?$',
|
||||||
|
testPathIgnorePatterns: ['/node_modules/', '/dist/', '/demo/'],
|
||||||
|
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
|
||||||
|
};
|
@ -1,59 +1,100 @@
|
|||||||
{
|
{
|
||||||
"name": "qrcode-decoder",
|
"name": "qrcode-decoder",
|
||||||
"version": "0.1.2",
|
"version": "0.3.4",
|
||||||
"description": "Tool for decoding qrcode",
|
"description": "Tool for decoding qrcode",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"jsnext:main": "dist/index.esm.js",
|
"browser": "dist/index.min.js",
|
||||||
|
"unpkg": "dist/index.min.js",
|
||||||
"module": "dist/index.esm.js",
|
"module": "dist/index.esm.js",
|
||||||
|
"types": "dist/index.d.ts",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
"packageManager": "pnpm@7.11.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"clean": "rimraf ./dist",
|
"build": "ts-node -P scripts/tsconfig.json scripts/bundle.ts commonjs,esm,aio && pnpm run dts && pnpm run copy-file",
|
||||||
"lint": "eslint src",
|
"dts": "tsc src/index.ts --declaration --emitDeclarationOnly --outDir './dist'",
|
||||||
"build:self": "rollup -c config/rollup.config.js",
|
"prettier": "prettier --check '**/*.{ts,tsx,md}' --config .prettierrc",
|
||||||
"build:esm": "rollup -c config/rollup.config.esm.js",
|
"prettier:fix": "prettier --write '**/*.{ts,tsx,md}' --config .prettierrc",
|
||||||
"build:aio": "rollup -c config/rollup.config.aio.js",
|
"test": "jest",
|
||||||
"build:aio.min": "NODE_ENV=production npm run build:aio",
|
"codecov": "jest --coverage && codecov",
|
||||||
"build": "npm run clean && npm run build:self && npm run build:esm && npm run build:aio && npm run build:aio.min && npm run copy-file",
|
"copy-file": "cp dist/index.min.js demo/lib",
|
||||||
"copy-file": "cp dist/index.aio.min.js demo/",
|
|
||||||
"test": "npm run lint && npm run build",
|
|
||||||
"zuul-test": "zuul --local 8080 --ui mocha-bdd -- test/test.js",
|
|
||||||
"release": "git commit -am $npm_package_version && git tag $npm_package_version && git push && git push --tags",
|
"release": "git commit -am $npm_package_version && git tag $npm_package_version && git push && git push --tags",
|
||||||
"deploy": "gh-pages -d demo --remote origin"
|
"pages": "gh-pages -d demo --remote origin",
|
||||||
|
"demo": "http-server demo -c-1"
|
||||||
|
},
|
||||||
|
"husky": {
|
||||||
|
"hooks": {
|
||||||
|
"pre-commit": "lint-staged"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.{ts,tsx,md}": [
|
||||||
|
"pnpm run prettier:fix",
|
||||||
|
"git add ."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/cli": "^7.7.0",
|
||||||
|
"@babel/core": "^7.7.2",
|
||||||
|
"@babel/plugin-proposal-class-properties": "^7.7.0",
|
||||||
|
"@babel/plugin-proposal-decorators": "^7.7.0",
|
||||||
|
"@babel/plugin-proposal-do-expressions": "^7.6.0",
|
||||||
|
"@babel/plugin-proposal-export-default-from": "^7.5.2",
|
||||||
|
"@babel/plugin-proposal-export-namespace-from": "^7.5.2",
|
||||||
|
"@babel/plugin-proposal-function-sent": "^7.7.0",
|
||||||
|
"@babel/plugin-proposal-json-strings": "^7.2.0",
|
||||||
|
"@babel/plugin-proposal-logical-assignment-operators": "^7.2.0",
|
||||||
|
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.4.4",
|
||||||
|
"@babel/plugin-proposal-numeric-separator": "^7.2.0",
|
||||||
|
"@babel/plugin-proposal-optional-chaining": "^7.6.0",
|
||||||
|
"@babel/plugin-proposal-pipeline-operator": "^7.5.0",
|
||||||
|
"@babel/plugin-proposal-throw-expressions": "^7.2.0",
|
||||||
|
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
||||||
|
"@babel/plugin-syntax-import-meta": "^7.2.0",
|
||||||
|
"@babel/plugin-transform-runtime": "^7.6.2",
|
||||||
|
"@babel/polyfill": "^7.7.0",
|
||||||
|
"@babel/preset-env": "^7.7.1",
|
||||||
|
"@babel/preset-typescript": "^7.7.2",
|
||||||
|
"@babel/runtime": "^7.7.2",
|
||||||
|
"@types/jest": "^24.9.0",
|
||||||
|
"@types/node": "^13.1.8",
|
||||||
|
"chalk": "^3.0.0",
|
||||||
|
"codecov": "^3.8.1",
|
||||||
|
"cross-env": "^6.0.3",
|
||||||
|
"fancy-log": "^1.3.3",
|
||||||
|
"gh-pages": "^3.1.0",
|
||||||
|
"http-server": "^14.1.0",
|
||||||
|
"husky": "^3.0.9",
|
||||||
|
"jest": "^24.9.0",
|
||||||
|
"lint-staged": "^9.5.0",
|
||||||
|
"prettier": "^1.18.2",
|
||||||
|
"rollup": "^2.38.1",
|
||||||
|
"rollup-plugin-commonjs": "^8.3.0",
|
||||||
|
"rollup-plugin-json": "^3.1.0",
|
||||||
|
"rollup-plugin-node-builtins": "^2.1.2",
|
||||||
|
"rollup-plugin-node-globals": "^1.4.0",
|
||||||
|
"rollup-plugin-node-resolve": "^3.4.0",
|
||||||
|
"rollup-plugin-typescript2": "0.17.1",
|
||||||
|
"rollup-plugin-uglify": "^6.0.4",
|
||||||
|
"ts-jest": "^24.3.0",
|
||||||
|
"ts-node": "^8.4.1",
|
||||||
|
"tslint": "^5.20.1",
|
||||||
|
"tslint-config-prettier": "^1.18.0",
|
||||||
|
"typescript": "^4.4.3"
|
||||||
},
|
},
|
||||||
"author": "yugasun",
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git://github.com/yugasun/qrcode-decoder.git"
|
"url": "git+https://github.com/yugasun/qrcode-decoder.git"
|
||||||
},
|
},
|
||||||
|
"keywords": [
|
||||||
|
"node-utils"
|
||||||
|
],
|
||||||
|
"author": "yugasun",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/yugasun/qrcode-decoder/issues"
|
"url": "https://github.com/yugasun/qrcode-decoder/issues"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"homepage": "https://github.com/yugasun/qrcode-decoder#readme",
|
||||||
"babel-core": "6.26.0",
|
|
||||||
"babel-plugin-transform-runtime": "6.23.0",
|
|
||||||
"babel-preset-env": "1.6.1",
|
|
||||||
"babel-preset-stage-2": "^6.24.1",
|
|
||||||
"cdkit": "1.1.0",
|
|
||||||
"es5-shim": "4.5.10",
|
|
||||||
"eslint": "4.18.2",
|
|
||||||
"eslint-config-airbnb-base": "^13.1.0",
|
|
||||||
"eslint-plugin-import": "^2.14.0",
|
|
||||||
"expect.js": "0.3.1",
|
|
||||||
"gh-pages": "^2.0.1",
|
|
||||||
"jsdom": "^13.0.0",
|
|
||||||
"mocha": "3.5.3",
|
|
||||||
"mocha-jsdom": "^2.0.0",
|
|
||||||
"rimraf": "2.6.2",
|
|
||||||
"rollup": "^0.67.3",
|
|
||||||
"rollup-plugin-babel": "3.0.3",
|
|
||||||
"rollup-plugin-commonjs": "8.3.0",
|
|
||||||
"rollup-plugin-node-resolve": "3.0.3",
|
|
||||||
"rollup-plugin-uglify": "^6.0.0",
|
|
||||||
"zuul": "^3.12.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"babel-polyfill": "^6.26.0",
|
"jsqr": "^1.4.0"
|
||||||
"jsqr": "^1.1.1"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,144 @@
|
|||||||
|
import * as path from 'path';
|
||||||
|
import * as rollup from 'rollup';
|
||||||
|
import resolve from 'rollup-plugin-node-resolve';
|
||||||
|
import typescript2 from 'rollup-plugin-typescript2';
|
||||||
|
import { uglify } from 'rollup-plugin-uglify';
|
||||||
|
import globals from 'rollup-plugin-node-globals';
|
||||||
|
import commonjs from 'rollup-plugin-commonjs';
|
||||||
|
import ts from 'typescript';
|
||||||
|
|
||||||
|
import { createLogger } from './logger';
|
||||||
|
|
||||||
|
const logBundle = createLogger('bundle');
|
||||||
|
|
||||||
|
const plugins: rollup.Plugin[] = [
|
||||||
|
resolve({
|
||||||
|
browser: true,
|
||||||
|
jsnext: true,
|
||||||
|
preferBuiltins: true,
|
||||||
|
}),
|
||||||
|
commonjs(),
|
||||||
|
typescript2({
|
||||||
|
tsconfig: path.join(__dirname, '..', 'tsconfig.json'),
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
async function bundleEsm() {
|
||||||
|
const bundle = await rollup.rollup({
|
||||||
|
input: path.join(process.cwd(), 'src/index.ts'),
|
||||||
|
plugins: [...plugins, globals()],
|
||||||
|
external: ['jsqr'],
|
||||||
|
});
|
||||||
|
await bundle.write({
|
||||||
|
file: 'dist/index.esm.js',
|
||||||
|
name: 'QrcodeDecoder',
|
||||||
|
format: 'esm',
|
||||||
|
sourcemap: false,
|
||||||
|
globals: {
|
||||||
|
jsqr: 'jsqr',
|
||||||
|
tslib: 'tslib',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async function bundleUmd() {
|
||||||
|
const bundle = await rollup.rollup({
|
||||||
|
input: path.join(process.cwd(), 'src/index.ts'),
|
||||||
|
plugins: [...plugins],
|
||||||
|
external: ['jsqr'],
|
||||||
|
});
|
||||||
|
await bundle.write({
|
||||||
|
file: 'dist/index.js',
|
||||||
|
exports: 'named',
|
||||||
|
name: 'QrcodeDecoder',
|
||||||
|
format: 'umd',
|
||||||
|
sourcemap: false,
|
||||||
|
globals: {
|
||||||
|
jsqr: 'jsqr',
|
||||||
|
tslib: 'tslib',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function bundleCommonjs() {
|
||||||
|
const bundle = await rollup.rollup({
|
||||||
|
input: path.join(process.cwd(), 'src/index.ts'),
|
||||||
|
plugins: [...plugins],
|
||||||
|
external: ['jsqr'],
|
||||||
|
});
|
||||||
|
await bundle.write({
|
||||||
|
file: 'dist/index.js',
|
||||||
|
exports: 'named',
|
||||||
|
name: 'QrcodeDecoder',
|
||||||
|
format: 'commonjs',
|
||||||
|
sourcemap: false,
|
||||||
|
globals: {
|
||||||
|
jsqr: 'jsqr',
|
||||||
|
tslib: 'tslib',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function bundleAio() {
|
||||||
|
const bundle = await rollup.rollup({
|
||||||
|
input: path.join(process.cwd(), 'src/index.ts'),
|
||||||
|
plugins: [
|
||||||
|
...plugins,
|
||||||
|
uglify({
|
||||||
|
compress: {
|
||||||
|
drop_debugger: true,
|
||||||
|
drop_console: false,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
await bundle.write({
|
||||||
|
file: 'dist/index.min.js',
|
||||||
|
name: 'QrcodeDecoder',
|
||||||
|
format: 'iife',
|
||||||
|
sourcemap: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function bundle() {
|
||||||
|
try {
|
||||||
|
const outputs = process.argv.slice(2)[0].split(',');
|
||||||
|
logBundle(`Creating bundle`);
|
||||||
|
|
||||||
|
if (outputs.indexOf('esm') === -1) {
|
||||||
|
logBundle(`Skipping esm`);
|
||||||
|
} else {
|
||||||
|
logBundle(`Creating esm`);
|
||||||
|
await bundleEsm();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outputs.indexOf('umd') === -1) {
|
||||||
|
logBundle(`Skipping umd`);
|
||||||
|
} else {
|
||||||
|
logBundle(`Creating umd`);
|
||||||
|
|
||||||
|
await bundleUmd();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outputs.indexOf('commonjs') === -1) {
|
||||||
|
logBundle(`Skipping commonjs`);
|
||||||
|
} else {
|
||||||
|
logBundle(`Creating commonjs`);
|
||||||
|
|
||||||
|
await bundleCommonjs();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outputs.indexOf('aio') === -1) {
|
||||||
|
logBundle(`Skipping umd aio`);
|
||||||
|
} else {
|
||||||
|
logBundle(`Creating umd aio`);
|
||||||
|
|
||||||
|
await bundleAio();
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logBundle('Failed to bundle:');
|
||||||
|
logBundle(err);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bundle();
|
@ -0,0 +1,16 @@
|
|||||||
|
import * as l from 'fancy-log';
|
||||||
|
const log = <typeof import('fancy-log')>(<any>(<any>l).default || l);
|
||||||
|
import * as c from 'chalk';
|
||||||
|
const chalk = <import('chalk').Chalk>(c.default || c);
|
||||||
|
|
||||||
|
export function createLogger(name: string): typeof log {
|
||||||
|
const prefix = `> ${chalk.green(name)} `;
|
||||||
|
const logger = <typeof log>log.bind(log, prefix);
|
||||||
|
logger.info = log.info.bind(log, prefix);
|
||||||
|
logger.dir = log.dir.bind(log, prefix);
|
||||||
|
logger.warn = log.warn.bind(log, prefix);
|
||||||
|
logger.error = log.error.bind(log, prefix);
|
||||||
|
return logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { chalk as c };
|
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"module": "commonjs",
|
||||||
|
"target": "es2015",
|
||||||
|
"resolveJsonModule": true
|
||||||
|
}
|
||||||
|
}
|
@ -1,376 +0,0 @@
|
|||||||
import 'babel-polyfill';
|
|
||||||
import jsQR from 'jsqr';
|
|
||||||
/* eslint-disable no-underscore-dangle */
|
|
||||||
class QrcodeDecoder {
|
|
||||||
/**
|
|
||||||
* Constructor for QrcodeDecoder
|
|
||||||
*/
|
|
||||||
constructor() {
|
|
||||||
this.timerCapture = null;
|
|
||||||
this.canvasElem = null;
|
|
||||||
this.gCtx = null;
|
|
||||||
this.stream = null;
|
|
||||||
this.videoElem = null;
|
|
||||||
this.getUserMediaHandler = null;
|
|
||||||
this.videoConstraints = { video: true, audio: false };
|
|
||||||
|
|
||||||
this.defaultOption = { inversionAttempts: 'attemptBoth' };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verifies if canvas element is supported.
|
|
||||||
*/
|
|
||||||
/* eslint-disable class-methods-use-this */
|
|
||||||
isCanvasSupported() {
|
|
||||||
const elem = document.createElement('canvas');
|
|
||||||
return !!(elem.getContext && elem.getContext('2d'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* draw lint
|
|
||||||
*
|
|
||||||
* @param {object} begin line begin point
|
|
||||||
* @param {object} end line end point
|
|
||||||
* @param {string} color color string
|
|
||||||
*/
|
|
||||||
// _drawLine(begin, end, color) {
|
|
||||||
// this.gCtx.beginPath();
|
|
||||||
// this.gCtx.moveTo(begin.x, begin.y);
|
|
||||||
// this.gCtx.lineTo(end.x, end.y);
|
|
||||||
// this.gCtx.lineWidth = 4;
|
|
||||||
// this.gCtx.strokeStyle = color;
|
|
||||||
// this.gCtx.stroke();
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* create qrcode marker
|
|
||||||
*
|
|
||||||
* @param {object} code jsqr parse code object
|
|
||||||
*/
|
|
||||||
// _createQrcodeMark(code) {
|
|
||||||
// this._drawLine(
|
|
||||||
// code.location.topLeftCorner,
|
|
||||||
// code.location.topRightCorner,
|
|
||||||
// '#FF3B58',
|
|
||||||
// );
|
|
||||||
// this._drawLine(
|
|
||||||
// code.location.topRightCorner,
|
|
||||||
// code.location.bottomRightCorner,
|
|
||||||
// '#FF3B58',
|
|
||||||
// );
|
|
||||||
// this._drawLine(
|
|
||||||
// code.location.bottomRightCorner,
|
|
||||||
// code.location.bottomLeftCorner,
|
|
||||||
// '#FF3B58',
|
|
||||||
// );
|
|
||||||
// this._drawLine(
|
|
||||||
// code.location.bottomLeftCorner,
|
|
||||||
// code.location.topLeftCorner,
|
|
||||||
// '#FF3B58',
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
_createImageData(target, width, height) {
|
|
||||||
if (!this.canvasElem) {
|
|
||||||
this._prepareCanvas(width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.gCtx.clearRect(0, 0, width, height);
|
|
||||||
this.gCtx.drawImage(target, 0, 0, width, height);
|
|
||||||
|
|
||||||
const imageData = this.gCtx.getImageData(
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
this.canvasElem.width,
|
|
||||||
this.canvasElem.height,
|
|
||||||
);
|
|
||||||
|
|
||||||
return imageData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepares the canvas element (which will
|
|
||||||
* receive the image from the camera and provide
|
|
||||||
* what the algorithm needs for checking for a
|
|
||||||
* QRCode and then decoding it.)
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param {DOMElement} canvasElem the canvas
|
|
||||||
* element
|
|
||||||
* @param {number} width The width that
|
|
||||||
* the canvas element
|
|
||||||
* should have
|
|
||||||
* @param {number} height The height that
|
|
||||||
* the canvas element
|
|
||||||
* should have
|
|
||||||
* @return {DOMElement} the canvas
|
|
||||||
* after the resize if width and height
|
|
||||||
* provided.
|
|
||||||
*/
|
|
||||||
_prepareCanvas(width, height) {
|
|
||||||
if (!this.canvasElem) {
|
|
||||||
this.canvasElem = document.createElement('canvas');
|
|
||||||
this.canvasElem.style.width = `${width}px`;
|
|
||||||
this.canvasElem.style.height = `${height}px`;
|
|
||||||
this.canvasElem.width = width;
|
|
||||||
this.canvasElem.height = height;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.gCtx = this.canvasElem.getContext('2d');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Based on the video dimensions and the canvas
|
|
||||||
* that was previously generated captures the
|
|
||||||
* video/image source and then paints into the
|
|
||||||
* canvas so that the decoder is able to work as
|
|
||||||
* it expects.
|
|
||||||
* @param {DOMElement} videoElem <video> dom element
|
|
||||||
* @param {Object} options options (optional) - Additional options.
|
|
||||||
* inversionAttempts - (attemptBoth (default), dontInvert, onlyInvert, or invertFirst)
|
|
||||||
* refer to jsqr options: https://github.com/cozmo/jsQR
|
|
||||||
*/
|
|
||||||
async _captureToCanvas(videoElem, options) {
|
|
||||||
if (this.timerCapture) {
|
|
||||||
clearTimeout(this.timerCapture);
|
|
||||||
}
|
|
||||||
const proms = () => {
|
|
||||||
const p = new Promise(async (resolve) => {
|
|
||||||
let code;
|
|
||||||
if (videoElem.videoWidth && videoElem.videoHeight) {
|
|
||||||
const imageData = this._createImageData(
|
|
||||||
videoElem,
|
|
||||||
videoElem.videoWidth,
|
|
||||||
videoElem.videoHeight,
|
|
||||||
);
|
|
||||||
|
|
||||||
code = jsQR(
|
|
||||||
imageData.data,
|
|
||||||
imageData.width,
|
|
||||||
imageData.height,
|
|
||||||
options,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (code) {
|
|
||||||
resolve(code);
|
|
||||||
} else {
|
|
||||||
this.timerCapture = setTimeout(async () => {
|
|
||||||
code = await this._captureToCanvas(
|
|
||||||
videoElem,
|
|
||||||
options,
|
|
||||||
);
|
|
||||||
resolve(code);
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.timerCapture = setTimeout(async () => {
|
|
||||||
code = await this._captureToCanvas(videoElem, options);
|
|
||||||
resolve(code);
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return p;
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = await proms();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepares the video element for receiving
|
|
||||||
* camera's input. Releases a stream if there
|
|
||||||
* was any (resets).
|
|
||||||
*
|
|
||||||
* @param {DOMElement} videoElem <video> dom element
|
|
||||||
* @param {Object} options options (optional) - Additional options.
|
|
||||||
* inversionAttempts - (attemptBoth (default), dontInvert, onlyInvert, or invertFirst)
|
|
||||||
* refer to jsqr options: https://github.com/cozmo/jsQR
|
|
||||||
*/
|
|
||||||
async decodeFromCamera(videoElem, options = {}) {
|
|
||||||
const opts = {
|
|
||||||
...this.defaultOption,
|
|
||||||
...options,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.stop();
|
|
||||||
if (!navigator.mediaDevices.getUserMedia) {
|
|
||||||
throw new Error("Couldn't get video from camera");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const stream = await navigator.mediaDevices.getUserMedia(
|
|
||||||
this.videoConstraints,
|
|
||||||
);
|
|
||||||
videoElem.srcObject = stream;
|
|
||||||
// videoElem.src = window.URL.createObjectURL(stream);
|
|
||||||
this.videoElem = videoElem;
|
|
||||||
this.stream = stream;
|
|
||||||
this.videoDimensions = false;
|
|
||||||
|
|
||||||
const code = await this.decodeFromVideo(videoElem, opts);
|
|
||||||
return code;
|
|
||||||
} catch (e) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepares the video element for video file.
|
|
||||||
*
|
|
||||||
* @param {DOMElement} videoElem <video> dom element
|
|
||||||
* @param {Object} options options (optional) - Additional options.
|
|
||||||
* inversionAttempts - (attemptBoth (default), dontInvert, onlyInvert, or invertFirst)
|
|
||||||
* refer to jsqr options: https://github.com/cozmo/jsQR
|
|
||||||
*/
|
|
||||||
async decodeFromVideo(videoElem, options = {}) {
|
|
||||||
const opts = {
|
|
||||||
...this.defaultOption,
|
|
||||||
...options,
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
this.videoElem = videoElem;
|
|
||||||
const code = await this._captureToCanvas(videoElem, opts);
|
|
||||||
return code;
|
|
||||||
} catch (e) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decodes an image from its src.
|
|
||||||
* @param {DOMElement} imageElem
|
|
||||||
* @param {Object} options options (optional) - Additional options.
|
|
||||||
* inversionAttempts - (attemptBoth (default), dontInvert, onlyInvert, or invertFirst)
|
|
||||||
* refer to jsqr options: https://github.com/cozmo/jsQR
|
|
||||||
*/
|
|
||||||
async decodeFromImage(img, options = {}) {
|
|
||||||
let imgDom;
|
|
||||||
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') {
|
|
||||||
imgDom = document.createElement('img');
|
|
||||||
imgDom.src = img;
|
|
||||||
if (options.crossOrigin) {
|
|
||||||
imgDom.crossOrigin = options.crossOrigin;
|
|
||||||
}
|
|
||||||
const proms = () => new Promise((resolve) => {
|
|
||||||
imgDom.onload = () => resolve(true);
|
|
||||||
});
|
|
||||||
await proms();
|
|
||||||
}
|
|
||||||
|
|
||||||
let code = false;
|
|
||||||
if (imgDom) {
|
|
||||||
code = this._decodeFromImageElm(imgDom, opts);
|
|
||||||
}
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
_decodeFromImageElm(imgObj, options = {}) {
|
|
||||||
const opts = {
|
|
||||||
...this.defaultOption,
|
|
||||||
...options,
|
|
||||||
};
|
|
||||||
const imageData = this._createImageData(
|
|
||||||
imgObj,
|
|
||||||
imgObj.width,
|
|
||||||
imgObj.height,
|
|
||||||
);
|
|
||||||
|
|
||||||
const code = jsQR(
|
|
||||||
imageData.data,
|
|
||||||
imageData.width,
|
|
||||||
imageData.height,
|
|
||||||
opts,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (code) {
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Releases a video stream that was being
|
|
||||||
* captured by prepareToVideo
|
|
||||||
*/
|
|
||||||
stop() {
|
|
||||||
if (this.stream) {
|
|
||||||
const track = this.stream.getTracks()[0];
|
|
||||||
track.stop();
|
|
||||||
this.stream = undefined;
|
|
||||||
|
|
||||||
// fix: clear black bg after camera capture
|
|
||||||
this.videoElem.srcObject = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.timerCapture) {
|
|
||||||
clearTimeout(this.timerCapture);
|
|
||||||
this.timerCapture = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the sourceId for the camera to use.
|
|
||||||
*
|
|
||||||
* The sourceId can be found using the
|
|
||||||
* getVideoSources function on a browser that
|
|
||||||
* supports it (currently only Chrome).
|
|
||||||
*
|
|
||||||
* @param {String} sourceId The id of the
|
|
||||||
* video source you want to use (or false to use
|
|
||||||
* the current default)
|
|
||||||
*/
|
|
||||||
setSourceId(sourceId) {
|
|
||||||
if (sourceId) {
|
|
||||||
this.videoConstraints.video = {
|
|
||||||
optional: [{ sourceId }],
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
this.videoConstraints.video = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a list of all available video sources on
|
|
||||||
* the current device.
|
|
||||||
* @param {Function} cb callback to be resolved
|
|
||||||
* with error (first param) ou results (second
|
|
||||||
* param) - a list containing all of the sources
|
|
||||||
* that are of the 'video' kind.
|
|
||||||
*/
|
|
||||||
getVideoSources() {
|
|
||||||
const sources = [];
|
|
||||||
|
|
||||||
if (!(MediaStreamTrack && MediaStreamTrack.getSources)) {
|
|
||||||
throw new Error(
|
|
||||||
'Current browser doest not support MediaStreamTrack.getSources',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
MediaStreamTrack.getSources((sourceInfos) => {
|
|
||||||
sourceInfos.forEach((sourceInfo) => {
|
|
||||||
if (sourceInfo.kind === 'video') {
|
|
||||||
sources.push(sourceInfo);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return sources;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default QrcodeDecoder;
|
|
@ -0,0 +1,356 @@
|
|||||||
|
import jsQR, { QRCode, Options } from 'jsqr';
|
||||||
|
|
||||||
|
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;
|
||||||
|
stream: null | MediaStream;
|
||||||
|
videoElem: null | HTMLVideoElement;
|
||||||
|
getUserMediaHandler: null;
|
||||||
|
videoConstraints: MediaStreamConstraints;
|
||||||
|
defaultOption: Options;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.timerCapture = null;
|
||||||
|
this.canvasElem = null;
|
||||||
|
this.gCtx = null;
|
||||||
|
this.stream = null;
|
||||||
|
this.videoElem = null;
|
||||||
|
this.getUserMediaHandler = null;
|
||||||
|
this.videoConstraints = {
|
||||||
|
// default use rear camera
|
||||||
|
video: { ...videoSize, facingMode: { exact: 'environment' } },
|
||||||
|
audio: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.defaultOption = { inversionAttempts: 'attemptBoth' };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies if canvas element is supported.
|
||||||
|
*/
|
||||||
|
isCanvasSupported() {
|
||||||
|
const elem = document.createElement('canvas');
|
||||||
|
return !!(elem.getContext && elem.getContext('2d'));
|
||||||
|
}
|
||||||
|
|
||||||
|
_createImageData(target: CanvasImageSource, width: number, height: number) {
|
||||||
|
if (!this.canvasElem) {
|
||||||
|
this._prepareCanvas(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.gCtx!.clearRect(0, 0, width, height);
|
||||||
|
this.gCtx!.drawImage(target, 0, 0, width, height);
|
||||||
|
|
||||||
|
const imageData = this.gCtx!.getImageData(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
this.canvasElem!.width,
|
||||||
|
this.canvasElem!.height,
|
||||||
|
);
|
||||||
|
|
||||||
|
return imageData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares the canvas element (which will
|
||||||
|
* receive the image from the camera and provide
|
||||||
|
* what the algorithm needs for checking for a
|
||||||
|
* QRCode and then decoding it.)
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param {DOMElement} canvasElem the canvas
|
||||||
|
* element
|
||||||
|
* @param {number} width The width that
|
||||||
|
* the canvas element
|
||||||
|
* should have
|
||||||
|
* @param {number} height The height that
|
||||||
|
* the canvas element
|
||||||
|
* should have
|
||||||
|
* @return {DOMElement} the canvas
|
||||||
|
* after the resize if width and height
|
||||||
|
* provided.
|
||||||
|
*/
|
||||||
|
_prepareCanvas(width: number, height: number) {
|
||||||
|
if (!this.canvasElem) {
|
||||||
|
this.canvasElem = document.createElement('canvas');
|
||||||
|
this.canvasElem.style.width = `${width}px`;
|
||||||
|
this.canvasElem.style.height = `${height}px`;
|
||||||
|
this.canvasElem.width = width;
|
||||||
|
this.canvasElem.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.gCtx = this.canvasElem.getContext('2d');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Based on the video dimensions and the canvas
|
||||||
|
* that was previously generated captures the
|
||||||
|
* video/image source and then paints into the
|
||||||
|
* canvas so that the decoder is able to work as
|
||||||
|
* it expects.
|
||||||
|
* @param {DOMElement} videoElem <video> dom element
|
||||||
|
* @param {Object} options options (optional) - Additional options.
|
||||||
|
* inversionAttempts - (attemptBoth (default), dontInvert, onlyInvert, or invertFirst)
|
||||||
|
* refer to jsqr options: https://github.com/cozmo/jsQR
|
||||||
|
*/
|
||||||
|
async _captureToCanvas(
|
||||||
|
videoElem: HTMLVideoElement,
|
||||||
|
options: Options,
|
||||||
|
): Promise<CodeResult> {
|
||||||
|
if (this.timerCapture) {
|
||||||
|
clearTimeout(this.timerCapture);
|
||||||
|
}
|
||||||
|
const proms = () => {
|
||||||
|
const p = new Promise(async (resolve) => {
|
||||||
|
let code;
|
||||||
|
if (videoElem.videoWidth && videoElem.videoHeight) {
|
||||||
|
const imageData = this._createImageData(
|
||||||
|
videoElem,
|
||||||
|
videoElem.videoWidth,
|
||||||
|
videoElem.videoHeight,
|
||||||
|
);
|
||||||
|
|
||||||
|
code = jsQR(
|
||||||
|
imageData.data,
|
||||||
|
imageData.width,
|
||||||
|
imageData.height,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (code) {
|
||||||
|
resolve(code);
|
||||||
|
} else {
|
||||||
|
this.timerCapture = setTimeout(async () => {
|
||||||
|
code = await this._captureToCanvas(videoElem, options);
|
||||||
|
resolve(code);
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.timerCapture = setTimeout(async () => {
|
||||||
|
code = await this._captureToCanvas(videoElem, options);
|
||||||
|
resolve(code);
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return p;
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await proms();
|
||||||
|
|
||||||
|
return result as CodeResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares the video element for receiving
|
||||||
|
* camera's input. Releases a stream if there
|
||||||
|
* was any (resets).
|
||||||
|
*
|
||||||
|
* @param {DOMElement} videoElem <video> dom element
|
||||||
|
* @param {Object} options options (optional) - Additional options.
|
||||||
|
* inversionAttempts - (attemptBoth (default), dontInvert, onlyInvert, or invertFirst)
|
||||||
|
* refer to jsqr options: https://github.com/cozmo/jsQR
|
||||||
|
*/
|
||||||
|
async decodeFromCamera(
|
||||||
|
videoElem: HTMLVideoElement,
|
||||||
|
options: any = {},
|
||||||
|
): Promise<CodeResult> {
|
||||||
|
const opts = {
|
||||||
|
...this.defaultOption,
|
||||||
|
...options,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.stop();
|
||||||
|
if (!navigator.mediaDevices.getUserMedia) {
|
||||||
|
throw new Error("Couldn't get video from camera");
|
||||||
|
}
|
||||||
|
|
||||||
|
let stream: MediaStream;
|
||||||
|
try {
|
||||||
|
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;
|
||||||
|
this.stream = stream;
|
||||||
|
|
||||||
|
const code = await this.decodeFromVideo(videoElem, opts);
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares the video element for video file.
|
||||||
|
*
|
||||||
|
* @param {DOMElement} videoElem <video> dom element
|
||||||
|
* @param {Object} options options (optional) - Additional options.
|
||||||
|
* inversionAttempts - (attemptBoth (default), dontInvert, onlyInvert, or invertFirst)
|
||||||
|
* refer to jsqr options: https://github.com/cozmo/jsQR
|
||||||
|
*/
|
||||||
|
async decodeFromVideo(
|
||||||
|
videoElem: HTMLVideoElement,
|
||||||
|
options = {},
|
||||||
|
): Promise<CodeResult> {
|
||||||
|
const opts = {
|
||||||
|
...this.defaultOption,
|
||||||
|
...options,
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
this.videoElem = videoElem;
|
||||||
|
const code = await this._captureToCanvas(videoElem, opts);
|
||||||
|
return code;
|
||||||
|
} catch (e) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes an image from its src.
|
||||||
|
* @param {DOMElement} imageElem
|
||||||
|
* @param {Object} options options (optional) - Additional options.
|
||||||
|
* inversionAttempts - (attemptBoth (default), dontInvert, onlyInvert, or invertFirst)
|
||||||
|
* refer to jsqr options: https://github.com/cozmo/jsQR
|
||||||
|
*/
|
||||||
|
async decodeFromImage(
|
||||||
|
img: HTMLImageElement | string,
|
||||||
|
options: { crossOrigin?: string } = {},
|
||||||
|
): Promise<CodeResult> {
|
||||||
|
let imgDom: HTMLImageElement | null = null;
|
||||||
|
const opts = {
|
||||||
|
...this.defaultOption,
|
||||||
|
...options,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof img === 'string') {
|
||||||
|
imgDom = document.createElement('img');
|
||||||
|
if (options.crossOrigin) {
|
||||||
|
imgDom.crossOrigin = options.crossOrigin;
|
||||||
|
}
|
||||||
|
imgDom.src = img;
|
||||||
|
const proms = () =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
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;
|
||||||
|
if (imgDom) {
|
||||||
|
code = this._decodeFromImageElm(imgDom, opts);
|
||||||
|
}
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
_decodeFromImageElm(imgObj: HTMLImageElement, options = {}) {
|
||||||
|
const opts: Options = {
|
||||||
|
...this.defaultOption,
|
||||||
|
...options,
|
||||||
|
};
|
||||||
|
const imageData = this._createImageData(
|
||||||
|
imgObj,
|
||||||
|
imgObj.width,
|
||||||
|
imgObj.height,
|
||||||
|
);
|
||||||
|
|
||||||
|
const code = jsQR(imageData.data, imageData.width, imageData.height, opts);
|
||||||
|
|
||||||
|
if (code) {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases a video stream that was being
|
||||||
|
* captured by prepareToVideo
|
||||||
|
*/
|
||||||
|
stop() {
|
||||||
|
if (this.stream) {
|
||||||
|
const track = this.stream.getTracks()[0];
|
||||||
|
track.stop();
|
||||||
|
this.stream = null;
|
||||||
|
|
||||||
|
// fix: clear black bg after camera capture
|
||||||
|
this.videoElem!.srcObject = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.timerCapture) {
|
||||||
|
clearTimeout(this.timerCapture);
|
||||||
|
this.timerCapture = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the sourceId for the camera to use.
|
||||||
|
*/
|
||||||
|
setGroupId(groupId: string) {
|
||||||
|
if (groupId) {
|
||||||
|
this.videoConstraints.video = {
|
||||||
|
advanced: [{ groupId }],
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
this.videoConstraints.video = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a list of all available video sources on
|
||||||
|
* the current device.
|
||||||
|
*/
|
||||||
|
async getVideoDevices() {
|
||||||
|
if (navigator.mediaDevices.enumerateDevices) {
|
||||||
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
||||||
|
return devices.filter((item: MediaDeviceInfo) => {
|
||||||
|
if (item.kind === 'videoinput') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
'Current browser doest not support MediaStreamTrack.getSources',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default QrcodeDecoder;
|
@ -1 +0,0 @@
|
|||||||
make test for your own private key
|
|
@ -1,37 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<title>Mocha</title>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<link rel="stylesheet" href="../../node_modules/mocha/mocha.css" />
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div id="mocha"></div>
|
|
||||||
<script src="../../node_modules/es5-shim/es5-shim.js"></script>
|
|
||||||
<script src="../../node_modules/es5-shim/es5-sham.js"></script>
|
|
||||||
|
|
||||||
<script src="../../node_modules/mocha/mocha.js"></script>
|
|
||||||
<script src="../../node_modules/expect.js/index.js"></script>
|
|
||||||
<script>
|
|
||||||
mocha.setup('bdd');
|
|
||||||
</script>
|
|
||||||
<script src="../../dist/index.aio.js"></script>
|
|
||||||
<script>
|
|
||||||
var libs = {
|
|
||||||
'expect.js': expect,
|
|
||||||
'../dist/index.js': window['qrcode-decoder']
|
|
||||||
};
|
|
||||||
var require = function(path) {
|
|
||||||
return libs[path];
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<script src="../test.js"></script>
|
|
||||||
<script>
|
|
||||||
mocha.run();
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
@ -1,57 +0,0 @@
|
|||||||
const expect = require('expect.js');
|
|
||||||
const QrcodeDecoder = require('../dist/index.js');
|
|
||||||
|
|
||||||
describe('QrcodeDecoder', function () {
|
|
||||||
// global.mocha.checkLeaks = false;
|
|
||||||
this.timeout(180000);
|
|
||||||
|
|
||||||
it('be defined', function () {
|
|
||||||
expect(QrcodeDecoder).to.be.ok();
|
|
||||||
});
|
|
||||||
|
|
||||||
var qr;
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
qr = new QrcodeDecoder();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('decodeFromImage', function () {
|
|
||||||
it('decode image from img element', function (done) {
|
|
||||||
const img = document.createElement('img');
|
|
||||||
img.src = 'demo/assets/qrcode.png';
|
|
||||||
img.onload = async function () {
|
|
||||||
const result = await qr.decodeFromImage(img);
|
|
||||||
expect(result.data).to.equal('192.168.1.13:3000');
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
it('decode image from img url', async function () {
|
|
||||||
const result = await qr.decodeFromImage('demo/assets/qrcode.png');
|
|
||||||
expect(result.data).to.equal('192.168.1.13:3000');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throw if no src in image element', async function (done) {
|
|
||||||
var img = document.createElement('img');
|
|
||||||
try {
|
|
||||||
await qr.decodeFromImage(img);
|
|
||||||
} catch(e) {
|
|
||||||
expect(e.message).to.equal('The ImageElement must contain a src')
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('decodeFromCamera', function () {
|
|
||||||
it('decode from a video with qrcode', async function () {
|
|
||||||
const video = document.createElement('video');
|
|
||||||
video.setAttribute('autoplay', true);
|
|
||||||
video.setAttribute('src', 'demo/assets/qrcode-video.mp4');
|
|
||||||
|
|
||||||
const result = await qr.decodeFromVideo(video);
|
|
||||||
expect(result.data).to.equal('192.168.1.13:3000');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist",
|
||||||
|
"baseUrl": ".",
|
||||||
|
"composite": true,
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"downlevelIteration": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"preserveConstEnums": true,
|
||||||
|
"importHelpers": false,
|
||||||
|
"lib": [
|
||||||
|
"esnext",
|
||||||
|
"dom"
|
||||||
|
],
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"sourceMap": false,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"strict": true,
|
||||||
|
"target": "es5"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"extends": ["tslint:latest", "tslint-config-prettier"],
|
||||||
|
"rules": {
|
||||||
|
"interface-name": [true, "never-prefix"],
|
||||||
|
"member-access": false,
|
||||||
|
"no-angle-bracket-type-assertion": false,
|
||||||
|
"no-bitwise": false,
|
||||||
|
"no-console": false,
|
||||||
|
"no-default-export": true,
|
||||||
|
"no-empty-interface": false,
|
||||||
|
"no-implicit-dependencies": false,
|
||||||
|
"no-submodule-imports": false,
|
||||||
|
"ordered-imports": [false],
|
||||||
|
"object-literal-sort-keys": false,
|
||||||
|
"object-literal-key-quotes": [true, "as-needed"],
|
||||||
|
"quotemark": [true, "single"],
|
||||||
|
"semicolon": [true, "always", "ignore-bound-class-methods"],
|
||||||
|
"jsx-boolean-value": false
|
||||||
|
},
|
||||||
|
"linterOptions": {
|
||||||
|
"exclude": ["config/**/*.js", "node_modules/**/*.ts"]
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue