@wemap/camera 3.2.4 → 3.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,7 +9,8 @@
9
9
 
10
10
  <body>
11
11
  <a href="./simple.html">Simple</a><br />
12
- <a href="./camera-logger.html">Camera-logger</a>
12
+ <a href="./camera-logger.html">Camera-logger</a><br />
13
+ <a href="./qr-code-scanner.html">QR Code Scanner</a>
13
14
  </body>
14
15
 
15
16
  </html>
@@ -0,0 +1,115 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Camera</title>
8
+
9
+ <script src="/js/camera-lib.js"></script>
10
+
11
+ <style type="text/css">
12
+ html,
13
+ body {
14
+ height: 100%;
15
+ margin: 0px;
16
+ }
17
+
18
+ #camera-container {
19
+ width: 100%;
20
+ height: 100%;
21
+ }
22
+
23
+ #controls {
24
+ position: absolute;
25
+ bottom: 0;
26
+ z-index: 2;
27
+ margin: 5px;
28
+ }
29
+
30
+ #controls>div {
31
+ background: #ffffffaa;
32
+ padding-left: 10px;
33
+ padding-right: 10px;
34
+ min-width: 100px;
35
+ display: inline-block;
36
+ }
37
+
38
+ #scan-result {
39
+ position: absolute;
40
+ border: 1px solid black;
41
+ background: white;
42
+ top: 0;
43
+ width: 150px;
44
+ left: 50%;
45
+ transform: translate(-50%, 0%);
46
+ z-index: 2;
47
+ text-align: center;
48
+ line-height: 30px;
49
+ vertical-align: middle;
50
+ }
51
+ </style>
52
+ </head>
53
+
54
+ <body>
55
+
56
+ <div id="camera-container"></div>
57
+ <div id="scan-result">Waiting</div>
58
+ <div id="controls">
59
+ <div id="camera-handler">
60
+ <div class="title">Camera</div>
61
+ <button id="start-camera">Start</button>
62
+ <button id="stop-camera" disabled>Stop</button>
63
+ </div>
64
+ <div id="qrcode-handler">
65
+ <div class="title">Scanner</div>
66
+ <button id="start-scanner">Start</button>
67
+ <button id="stop-scanner" disabled>Stop</button>
68
+ </div>
69
+ </div>
70
+
71
+
72
+ <script>
73
+ /* global Camera, QrCodeScanner */
74
+
75
+ const cameraContainer = document.getElementById('camera-container');
76
+ const scanResultContainer = document.getElementById('scan-result');
77
+ const startCameraButton = document.getElementById('start-camera');
78
+ const stopCameraButton = document.getElementById('stop-camera');
79
+ const startScannerButton = document.getElementById('start-scanner');
80
+ const stopScannerButton = document.getElementById('stop-scanner');
81
+
82
+ const camera = new Camera(cameraContainer);
83
+ const qrCodeScanner = new QrCodeScanner(camera);
84
+ qrCodeScanner.on('scan', str => (scanResultContainer.innerHTML = str));
85
+
86
+
87
+ startCameraButton.addEventListener('click', () => {
88
+ camera.start();
89
+ startCameraButton.disabled = true;
90
+ stopCameraButton.disabled = false;
91
+ });
92
+
93
+ stopCameraButton.addEventListener('click', () => {
94
+ camera.stop();
95
+ startCameraButton.disabled = false;
96
+ stopCameraButton.disabled = true;
97
+ });
98
+
99
+
100
+ startScannerButton.addEventListener('click', () => {
101
+ qrCodeScanner.start();
102
+ startScannerButton.disabled = true;
103
+ stopScannerButton.disabled = false;
104
+ });
105
+
106
+ stopScannerButton.addEventListener('click', () => {
107
+ qrCodeScanner.stop();
108
+ startScannerButton.disabled = false;
109
+ stopScannerButton.disabled = true;
110
+ scanResultContainer.innerHTML = 'Waiting';
111
+ });
112
+ </script>
113
+ </body>
114
+
115
+ </html>
package/index.js CHANGED
@@ -1,3 +1,8 @@
1
+ import { BarcodeFormat } from '@zxing/library';
1
2
  import Camera from './src/Camera.js';
3
+ import QrCodeScanner from './src/QrCodeScanner.js';
4
+ import SharedCameras from './src/SharedCameras.js';
2
5
 
3
- export { Camera };
6
+ export {
7
+ BarcodeFormat, Camera, QrCodeScanner, SharedCameras
8
+ };
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "directory": "packages/camera"
12
12
  },
13
13
  "name": "@wemap/camera",
14
- "version": "3.2.4",
14
+ "version": "3.2.6",
15
15
  "bugs": {
16
16
  "url": "https://github.com/wemap/wemap-modules-js/issues"
17
17
  },
@@ -23,9 +23,10 @@
23
23
  ],
24
24
  "license": "ISC",
25
25
  "dependencies": {
26
- "@wemap/navigation-logger": "^3.2.4",
26
+ "@wemap/navigation-logger": "^3.2.6",
27
+ "@zxing/library": "^0.17.1",
27
28
  "events": "^3.1.0"
28
29
  },
29
30
  "type": "module",
30
- "gitHead": "2b6080727464a2b671cd173a317ffd7bb196f789"
31
+ "gitHead": "0b4bcb307a86aa10d123603698961463baf931fd"
31
32
  }
package/src/Camera.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import EventEmitter from 'events';
2
2
 
3
3
  import CameraLogger from './CameraLogger.js';
4
+ import SharedCameras from './SharedCameras.js';
4
5
 
5
6
  const GENERIC_HARDWARE_VERTICAL_FOV = 60;
6
7
 
@@ -31,16 +32,22 @@ class Camera extends EventEmitter {
31
32
 
32
33
  fov = null;
33
34
 
34
- _videoPlaying = false;
35
+ _isStarted = false;
35
36
 
36
37
  _hardwareVerticalFov = GENERIC_HARDWARE_VERTICAL_FOV;
37
38
  _resizeOnWindowChange;
38
39
 
39
40
  _cameraLogger;
40
41
 
42
+ /**
43
+ * @param {Node} container
44
+ * @param {boolean} resizeOnWindowChange
45
+ */
41
46
  constructor(container, resizeOnWindowChange = false) {
42
47
  super();
43
48
 
49
+ SharedCameras._add(this, container);
50
+
44
51
  this.videoContainer = container;
45
52
  this._resizeOnWindowChange = resizeOnWindowChange;
46
53
 
@@ -58,11 +65,11 @@ class Camera extends EventEmitter {
58
65
 
59
66
  async start() {
60
67
 
61
- if (this.videoPlaying) {
68
+ if (this._isStarted) {
62
69
  throw new Error('Camera is already started');
63
70
  }
64
71
 
65
- this.videoPlaying = true;
72
+ this._isStarted = true;
66
73
 
67
74
  await Camera.checkAvailability();
68
75
 
@@ -83,14 +90,18 @@ class Camera extends EventEmitter {
83
90
  }
84
91
 
85
92
  this._cameraLogger.attachStream(this.videoStream);
93
+
94
+ this.emit('start');
86
95
  }
87
96
 
88
97
  async stop() {
89
98
 
90
- if (!this.videoPlaying) {
99
+ if (!this._isStarted) {
91
100
  throw new Error('Camera is not started yet');
92
101
  }
93
102
 
103
+ this.emit('stop');
104
+
94
105
  this._cameraLogger.detachStream();
95
106
 
96
107
  if (this.videoStream && this.videoStream.stop) {
@@ -106,7 +117,7 @@ class Camera extends EventEmitter {
106
117
  if (this._resizeOnWindowChange) {
107
118
  window.removeEventListener('resize', this.notifyContainerSizeChanged);
108
119
  }
109
- this.videoPlaying = false;
120
+ this._isStarted = false;
110
121
  }
111
122
 
112
123
  static async checkAvailability(testUserMedia = false) {
@@ -138,6 +149,10 @@ class Camera extends EventEmitter {
138
149
  }
139
150
  }
140
151
 
152
+ get isStarted() {
153
+ return this._isStarted;
154
+ }
155
+
141
156
  set hardwareVerticalFov(hardwareVerticalFov) {
142
157
  this._hardwareVerticalFov = hardwareVerticalFov;
143
158
  this._computeFov();
@@ -0,0 +1,149 @@
1
+ /* eslint-disable max-statements */
2
+ import EventEmitter from 'events';
3
+ import {
4
+ MultiFormatReader,
5
+ BarcodeFormat,
6
+ DecodeHintType,
7
+ RGBLuminanceSource,
8
+ BinaryBitmap,
9
+ HybridBinarizer
10
+ } from '@zxing/library';
11
+
12
+ import Camera from './Camera.js';
13
+
14
+ class QrCodeScanner extends EventEmitter {
15
+
16
+ static DEFAULT_FORMATS = [
17
+ BarcodeFormat.QR_CODE,
18
+ BarcodeFormat.DATA_MATRIX,
19
+ BarcodeFormat.CODABAR
20
+ ];
21
+
22
+ /**
23
+ * @type {Camera}
24
+ */
25
+ _camera;
26
+
27
+ /**
28
+ * @type {HTMLVideoElement}
29
+ */
30
+ _videoElement;
31
+
32
+ /**
33
+ * @type {HTMLCanvasElement}
34
+ */
35
+ _canvas;
36
+
37
+ /**
38
+ * @type {CanvasRenderingContext2D}
39
+ */
40
+ _ctx;
41
+
42
+ /**
43
+ * @type {MultiFormatReader}
44
+ */
45
+ _codeReader;
46
+
47
+ /**
48
+ * @type {number}
49
+ */
50
+ _intervalTime;
51
+
52
+ /**
53
+ * @type {number}
54
+ */
55
+ _intervalTick;
56
+
57
+ /**
58
+ * @type {boolean}
59
+ */
60
+ _isStarted = false;
61
+
62
+
63
+ /**
64
+ * @param {Camera} camera
65
+ */
66
+ constructor(camera) {
67
+ super();
68
+
69
+ this._camera = camera;
70
+ this._videoElement = camera.videoElement;
71
+
72
+ this._codeReader = new MultiFormatReader();
73
+
74
+ this._canvas = document.createElement('canvas');
75
+ this._ctx = this._canvas.getContext('2d');
76
+
77
+ this._camera.on('start', () => this._tryStart());
78
+ this._camera.on('stop', () => this._stop());
79
+ }
80
+
81
+ start(intervalTime = 500, formats = QrCodeScanner.DEFAULT_FORMATS) {
82
+ this._intervalTime = intervalTime;
83
+
84
+ if (this._isStarted) {
85
+ return;
86
+ }
87
+ this._isStarted = true;
88
+
89
+ const hints = new Map();
90
+ hints.set(DecodeHintType.POSSIBLE_FORMATS, formats);
91
+ this._codeReader.setHints(hints);
92
+
93
+ this._tryStart();
94
+ }
95
+
96
+ stop() {
97
+ this._isStarted = false;
98
+ this._stop();
99
+ }
100
+
101
+ _tryStart() {
102
+
103
+ if (!this._isStarted || !this._camera.isStarted) {
104
+ return;
105
+ }
106
+
107
+ this._width = this._videoElement.videoWidth;
108
+ this._height = this._videoElement.videoHeight;
109
+ this._canvas.width = this._width;
110
+ this._canvas.height = this._height;
111
+ this._luminances = new Uint8Array(this._width * this._height);
112
+
113
+ this._intervalTick = setInterval(() => this._onFrame(), this._intervalTime);
114
+ }
115
+
116
+ _stop() {
117
+ clearInterval(this._intervalTick);
118
+ }
119
+
120
+ _onFrame() {
121
+
122
+ if (!this._width || !this._height) {
123
+ return;
124
+ }
125
+
126
+ this._ctx.drawImage(this._videoElement, 0, 0, this._width, this._height);
127
+ const img = this._ctx.getImageData(0, 0, this._width, this._height);
128
+
129
+ for (let i = 0; i < this._luminances.length; i++) {
130
+ // eslint-disable-next-line no-bitwise
131
+ this._luminances[i] = ((img.data[i * 4] + img.data[i * 4 + 1] * 2 + img.data[i * 4 + 2]) / 4) & 0xFF;
132
+ }
133
+
134
+ const binaryBitmap = new BinaryBitmap(
135
+ new HybridBinarizer(
136
+ new RGBLuminanceSource(this._luminances, this._width, this._height)
137
+ )
138
+ );
139
+
140
+ try {
141
+ const result = this._codeReader.decodeWithState(binaryBitmap);
142
+ this.emit('scan', result.getText());
143
+ } catch (e) {
144
+ // do nothing
145
+ }
146
+ }
147
+ }
148
+
149
+ export default QrCodeScanner;
@@ -0,0 +1,34 @@
1
+ import Camera from './Camera.js';
2
+
3
+ class SharedCameras {
4
+
5
+ static _list = [];
6
+
7
+ /**
8
+ * @param {Camera} camera
9
+ * @param {Node}
10
+ */
11
+ static _add(camera, container) {
12
+ this._list.push({
13
+ camera,
14
+ container
15
+ });
16
+ }
17
+
18
+ static get list() {
19
+ return this._list;
20
+ }
21
+
22
+ static getCameraByContainer(container) {
23
+ for (const {
24
+ camera, container: _container
25
+ } of this._list) {
26
+ if (container === _container) {
27
+ return camera;
28
+ }
29
+ }
30
+ return null;
31
+ }
32
+ }
33
+
34
+ export default SharedCameras;