feat: init project
parent
28cf46138a
commit
82f77ad9e7
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
["env",
|
||||||
|
{
|
||||||
|
"targets": {
|
||||||
|
"browsers": "last 2 versions, > 1%, ie >= 6, Android >= 4, iOS >= 6, and_uc > 9",
|
||||||
|
"node": "0.10"
|
||||||
|
},
|
||||||
|
"modules": false,
|
||||||
|
"loose": false
|
||||||
|
}],
|
||||||
|
"stage-2"
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
["transform-runtime", {
|
||||||
|
"helpers": false,
|
||||||
|
"polyfill": false,
|
||||||
|
"regenerator": false,
|
||||||
|
"moduleName": "babel-runtime"
|
||||||
|
}]
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[{*.js,*.css,*.html}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[{package.json,.*rc,*.yml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
@ -0,0 +1 @@
|
|||||||
|
test
|
@ -0,0 +1,45 @@
|
|||||||
|
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,3 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
.DS_Store
|
@ -0,0 +1,11 @@
|
|||||||
|
node_modules
|
||||||
|
config
|
||||||
|
demo
|
||||||
|
src
|
||||||
|
test
|
||||||
|
.babelrc
|
||||||
|
.editorconfig
|
||||||
|
.travis.yml
|
||||||
|
CHANGELOG.md
|
||||||
|
package-lock.json
|
||||||
|
TODO.md
|
@ -0,0 +1,3 @@
|
|||||||
|
language: node_js
|
||||||
|
node_js:
|
||||||
|
- "8"
|
@ -0,0 +1 @@
|
|||||||
|
# Change Log
|
@ -0,0 +1,128 @@
|
|||||||
|
# [qrcode-decoder](https://github.com/yugasun/qrcode-decoder)
|
||||||
|
|
||||||
|
[](https://github.com/yugasun/qrcode-decoder/blob/master/LICENSE)
|
||||||
|
[](https://travis-ci.org/yugasun/qrcode-decoder)
|
||||||
|
[](http://www.npmtrends.com/qrcode-decoder)
|
||||||
|
|
||||||
|
[简体中文](./README.zh-CN.md) | English
|
||||||
|
|
||||||
|
A tool for decoding qrcode.
|
||||||
|
|
||||||
|
## Directory
|
||||||
|
|
||||||
|
```
|
||||||
|
.
|
||||||
|
├── demo code demo
|
||||||
|
├── dist build output
|
||||||
|
├── doc docs
|
||||||
|
├── src source code
|
||||||
|
├── test unit test
|
||||||
|
├── CHANGELOG.md change log
|
||||||
|
└── TODO.md todo list
|
||||||
|
```
|
||||||
|
|
||||||
|
## Guide
|
||||||
|
|
||||||
|
Use `npm` to install.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install --save qrcode-decoder
|
||||||
|
```
|
||||||
|
|
||||||
|
Using in webpack:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import QrcodeDecoder from 'qrcode-decoder';
|
||||||
|
```
|
||||||
|
|
||||||
|
Using in browser:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script src="node_modules/qrcode-decoder/dist/index.aio.js"></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
|
||||||
|
### QrcodeDecoder()
|
||||||
|
|
||||||
|
User `new` to create a decoder object.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var qr = new QrcodeDecoder();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### decodeFromImage(img, options)
|
||||||
|
|
||||||
|
Decodes an image from url or an `<img>` element with a `src` attribute set.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
qr.decodeFromImage(img).then((res) => {
|
||||||
|
console.log(res);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
[Demo](./demo/image.html)
|
||||||
|
|
||||||
|
#### decodeFromVideo(videoElem, options)
|
||||||
|
|
||||||
|
Decodes directly from a video with a well specified `src` attribute
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
qr.decodeFromVideo(videoElement).then((res) => {
|
||||||
|
console.log(res);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
[Demo](./demo/video.html)
|
||||||
|
|
||||||
|
#### decodeFromCamera(videoElem, options)
|
||||||
|
|
||||||
|
Decodes from a videoElement.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
qr.decodeFromCamera(videoElem).then((res) => {
|
||||||
|
console.log(res);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
[Demo](./demo/camera.html)
|
||||||
|
|
||||||
|
#### stop()
|
||||||
|
|
||||||
|
Stops the current `qr` from searching for a QRCode.
|
||||||
|
|
||||||
|
## Develop
|
||||||
|
|
||||||
|
Install dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
Build code:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
Run unit test:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
Modify version in `package.json`, run `release` script:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm run release
|
||||||
|
```
|
||||||
|
|
||||||
|
Publish
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm publish
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[MIT](./LICENSE)
|
@ -0,0 +1,5 @@
|
|||||||
|
# TODO
|
||||||
|
|
||||||
|
Rewrite unit test.
|
||||||
|
|
||||||
|
- [] Rewrite unit test.
|
@ -0,0 +1,32 @@
|
|||||||
|
// rollup.config.js
|
||||||
|
|
||||||
|
var babel = require('rollup-plugin-babel');
|
||||||
|
var nodeResolve = require('rollup-plugin-node-resolve');
|
||||||
|
var commonjs = require('rollup-plugin-commonjs');
|
||||||
|
|
||||||
|
var common = require('./rollup.js');
|
||||||
|
|
||||||
|
export default {
|
||||||
|
input: 'src/index.js',
|
||||||
|
output: {
|
||||||
|
file: '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/**',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
};
|
@ -0,0 +1,7 @@
|
|||||||
|
import config from './rollup.config';
|
||||||
|
|
||||||
|
// ES output
|
||||||
|
config.output.format = 'es';
|
||||||
|
config.output.file = 'dist/index.esm.js';
|
||||||
|
|
||||||
|
export default config;
|
@ -0,0 +1,21 @@
|
|||||||
|
// rollup.config.js
|
||||||
|
|
||||||
|
var babel = require('rollup-plugin-babel');
|
||||||
|
var common = require('./rollup.js');
|
||||||
|
|
||||||
|
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/**'
|
||||||
|
})
|
||||||
|
]
|
||||||
|
};
|
@ -0,0 +1,17 @@
|
|||||||
|
var pkg = require('../package.json');
|
||||||
|
|
||||||
|
// 兼容 qrcode-decoder 和 @yugasun/qrcode-decoder
|
||||||
|
var name = pkg.name.split('/').pop();
|
||||||
|
var version = pkg.version;
|
||||||
|
|
||||||
|
var banner =
|
||||||
|
`/*!
|
||||||
|
* 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)
|
||||||
|
*/
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports.name = name;
|
||||||
|
exports.banner = banner;
|
@ -0,0 +1,39 @@
|
|||||||
|
<!DOCTYPE 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="../dist/index.aio.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
function main() {
|
||||||
|
var qr = new QrcodeDecoder();
|
||||||
|
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);
|
||||||
|
console.log('code', code);
|
||||||
|
result.innerText = "Result: " + code.data;
|
||||||
|
}
|
||||||
|
start.onclick = startScan;
|
||||||
|
|
||||||
|
stop.onclick = function() {
|
||||||
|
qr.stop();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
main();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,31 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>QrcodeDecoder - Image</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<button id="decode">Decode!</button><br>
|
||||||
|
<span id="result"></span><br>
|
||||||
|
<!-- <img src="../test/assets/qrcode.png" alt="qr code" /> -->
|
||||||
|
<script src="../dist/index.aio.js"></script>
|
||||||
|
<script type="module">
|
||||||
|
function main() {
|
||||||
|
var qr = new QrcodeDecoder();
|
||||||
|
|
||||||
|
var btn = document.querySelector('button#decode');
|
||||||
|
var img = document.querySelector('img');
|
||||||
|
var result = document.querySelector('#result');
|
||||||
|
|
||||||
|
btn.onclick = async () => {
|
||||||
|
// you can also decode from image path
|
||||||
|
const code = await qr.decodeFromImage('../test/assets/qrcode.png');
|
||||||
|
// const code = qr.decodeFromImage(img);
|
||||||
|
console.log(code);
|
||||||
|
result.innerText = code.data;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
main();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,18 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>QrcodeDecoder</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>QrcodeDecoder</h1>
|
||||||
|
<h2>Examples</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="camera.html">Camera</a></li>
|
||||||
|
<li><a href="image.html">Image</a></li>
|
||||||
|
<li><a href="video.html">Video</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,39 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<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="../test/assets/qrcode-video.mp4"></video>
|
||||||
|
|
||||||
|
<script src="../dist/index.aio.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
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();
|
||||||
|
|
||||||
|
start.onclick = startScan;
|
||||||
|
|
||||||
|
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>
|
||||||
|
</html>
|
@ -0,0 +1,49 @@
|
|||||||
|
# Api
|
||||||
|
|
||||||
|
### QrcodeDecoder()
|
||||||
|
|
||||||
|
User `new` to create a decoder object.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var qr = new QrcodeDecoder();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### decodeFromImage(img, options)
|
||||||
|
|
||||||
|
Decodes an image from url or an `<img>` element with a `src` attribute set.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
qr.decodeFromImage(img).then((res) => {
|
||||||
|
console.log(res);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
[Demo](./demo/image.html)
|
||||||
|
|
||||||
|
#### decodeFromVideo(videoElem, options)
|
||||||
|
|
||||||
|
Decodes directly from a video with a well specified `src` attribute
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
qr.decodeFromVideo(videoElement).then((res) => {
|
||||||
|
console.log(res);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
[Demo](./demo/video.html)
|
||||||
|
|
||||||
|
#### decodeFromCamera(videoElem, options)
|
||||||
|
|
||||||
|
Decodes from a videoElement.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
qr.decodeFromCamera(videoElem).then((res) => {
|
||||||
|
console.log(res);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
[Demo](./demo/camera.html)
|
||||||
|
|
||||||
|
#### stop()
|
||||||
|
|
||||||
|
Stops the current `qr` from searching for a QRCode.
|
@ -0,0 +1,49 @@
|
|||||||
|
# Api
|
||||||
|
|
||||||
|
### QrcodeDecoder()
|
||||||
|
|
||||||
|
通过 `new` 关键字生成处理对象。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var qr = new QrcodeDecoder();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### decodeFromImage(img, options)
|
||||||
|
|
||||||
|
解析页面中的图片二维码。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
qr.decodeFromImage(img).then((res) => {
|
||||||
|
console.log(res);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
[Demo](./demo/image.html)
|
||||||
|
|
||||||
|
#### decodeFromVideo(videoElem, options)
|
||||||
|
|
||||||
|
解析页面中的视频中的二维码。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
qr.decodeFromVideo(videoElement).then((res) => {
|
||||||
|
console.log(res);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
[Demo](./demo/video.html)
|
||||||
|
|
||||||
|
#### decodeFromCamera(videoElem, options)
|
||||||
|
|
||||||
|
通过获取摄像头视频来扫描解析二维码。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
qr.decodeFromCamera(videoElem).then((res) => {
|
||||||
|
console.log(res);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
[Demo](./demo/camera.html)
|
||||||
|
|
||||||
|
#### stop()
|
||||||
|
|
||||||
|
停止当前视频捕获。
|
@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
"name": "qrcode-decoder",
|
||||||
|
"version": "1.1.0",
|
||||||
|
"description": "Tool for decoding qrcode",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"jsnext:main": "dist/index.esm.js",
|
||||||
|
"module": "dist/index.esm.js",
|
||||||
|
"sideEffects": false,
|
||||||
|
"scripts": {
|
||||||
|
"clean": "rimraf ./dist",
|
||||||
|
"lint": "eslint src",
|
||||||
|
"build:self": "rollup -c config/rollup.config.js",
|
||||||
|
"build:esm": "rollup -c config/rollup.config.esm.js",
|
||||||
|
"build:aio": "rollup -c config/rollup.config.aio.js",
|
||||||
|
"build": "npm run clean && npm run build:self && npm run build:esm && npm run build:aio",
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"author": "yugasun",
|
||||||
|
"license": "MIT",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git://github.com/yugasun/qrcode-decoder.git"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/yugasun/qrcode-decoder/issues"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"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",
|
||||||
|
"jsdom": "^13.0.0",
|
||||||
|
"mocha": "3.5.3",
|
||||||
|
"mocha-jsdom": "^2.0.0",
|
||||||
|
"rimraf": "2.6.2",
|
||||||
|
"rollup": "0.57.1",
|
||||||
|
"rollup-plugin-babel": "3.0.3",
|
||||||
|
"rollup-plugin-commonjs": "8.3.0",
|
||||||
|
"rollup-plugin-node-resolve": "3.0.3",
|
||||||
|
"zuul": "^3.12.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"babel-polyfill": "^6.26.0",
|
||||||
|
"jsqr": "^1.1.1"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,370 @@
|
|||||||
|
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;
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 @@
|
|||||||
|
make test for your own private key
|
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 935 B |
@ -0,0 +1,37 @@
|
|||||||
|
<!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>
|
@ -0,0 +1,57 @@
|
|||||||
|
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 = 'test/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('test/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', 'test/assets/qrcode-video.mp4');
|
||||||
|
|
||||||
|
const result = await qr.decodeFromVideo(video);
|
||||||
|
expect(result.data).to.equal('192.168.1.13:3000');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in New Issue