@smileid/web-components 1.0.1-beta → 1.4.2
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.
- package/components/camera-permission/CameraPermission.js +6 -2
- package/components/camera-permission/CameraPermission.stories.js +10 -4
- package/components/document/src/DocumentCaptureScreens.js +15 -9
- package/components/document/src/DocumentCaptureScreens.stories.js +16 -12
- package/components/document/src/document-capture/DocumentCapture.js +295 -217
- package/components/document/src/document-capture/DocumentCapture.stories.js +9 -2
- package/components/document/src/document-capture-instructions/DocumentCaptureInstructions.js +33 -33
- package/components/document/src/document-capture-instructions/DocumentCaptureInstructions.stories.js +8 -1
- package/components/document/src/document-capture-review/DocumentCaptureReview.js +15 -31
- package/components/document/src/document-capture-review/DocumentCaptureReview.stories.js +12 -2
- package/components/end-user-consent/src/EndUserConsent.js +14 -31
- package/components/end-user-consent/src/EndUserConsent.stories.js +8 -2
- package/components/navigation/src/Navigation.js +10 -2
- package/components/selfie/src/SelfieCaptureScreens.js +36 -10
- package/components/selfie/src/SelfieCaptureScreens.stories.js +13 -4
- package/components/selfie/src/selfie-capture/SelfieCapture.js +94 -23
- package/components/selfie/src/selfie-capture/SelfieCapture.stories.js +14 -1
- package/components/selfie/src/selfie-capture-instructions/SelfieCaptureInstructions.js +50 -44
- package/components/selfie/src/selfie-capture-instructions/SelfieCaptureInstructions.stories.js +8 -1
- package/components/selfie/src/selfie-capture-review/SelfieCaptureReview.js +3 -4
- package/components/selfie/src/selfie-capture-review/SelfieCaptureReview.stories.js +8 -1
- package/components/signature-pad/package-lock.json +3009 -0
- package/components/signature-pad/package.json +6 -6
- package/components/signature-pad/src/SignaturePad.js +5 -1
- package/components/signature-pad/src/SignaturePad.stories.js +10 -2
- package/components/smart-camera-web/src/SmartCameraWeb.js +53 -8
- package/components/smart-camera-web/src/SmartCameraWeb.stories.js +22 -9
- package/components/totp-consent/src/TotpConsent.js +15 -3
- package/cypress/e2e/smart-camera-web-agent-mode.cy.js +144 -0
- package/cypress/e2e/smart-camera-web-complete-flow.cy.js +221 -0
- package/cypress/e2e/smart-camera-web.cy.js +1 -1
- package/cypress/pages/smart-camera-web-agent-mode.html +36 -0
- package/cypress/pages/smart-camera-web-complete-flow.html +42 -0
- package/domain/camera/src/SmartCamera.js +28 -0
- package/package.json +8 -8
- package/styles/src/styles.js +14 -3
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import SmartCamera from '../../../../domain/camera/src/SmartCamera';
|
|
2
2
|
import styles from '../../../../styles/src/styles';
|
|
3
|
-
import {
|
|
4
|
-
PORTRAIT_ID_PREVIEW_HEIGHT,
|
|
5
|
-
PORTRAIT_ID_PREVIEW_WIDTH,
|
|
6
|
-
} from '../../../../domain/constants/src/Constants';
|
|
7
3
|
import '../../../navigation/src';
|
|
8
4
|
|
|
9
5
|
function hasMoreThanNColors(data, n = 16) {
|
|
@@ -20,7 +16,6 @@ function hasMoreThanNColors(data, n = 16) {
|
|
|
20
16
|
|
|
21
17
|
function templateString() {
|
|
22
18
|
return `
|
|
23
|
-
${styles}
|
|
24
19
|
<style>
|
|
25
20
|
.visually-hidden {
|
|
26
21
|
border: 0;
|
|
@@ -42,12 +37,12 @@ function templateString() {
|
|
|
42
37
|
width: 100%;
|
|
43
38
|
}
|
|
44
39
|
|
|
45
|
-
|
|
40
|
+
.id-video.mobile-camera-screen {
|
|
46
41
|
display: flex;
|
|
47
42
|
align-items: stretch;
|
|
48
43
|
justify-content: center;
|
|
49
|
-
max-height:
|
|
50
|
-
height:
|
|
44
|
+
max-height: 300px;
|
|
45
|
+
height: 15rem;
|
|
51
46
|
width: 100%;
|
|
52
47
|
overflow: visible;
|
|
53
48
|
margin: 0 auto;
|
|
@@ -55,7 +50,7 @@ function templateString() {
|
|
|
55
50
|
|
|
56
51
|
@media (max-width: 600px) {
|
|
57
52
|
.section {
|
|
58
|
-
width:
|
|
53
|
+
width: 99%;
|
|
59
54
|
height: 100vh;
|
|
60
55
|
justify-content: center;
|
|
61
56
|
}
|
|
@@ -88,25 +83,22 @@ function templateString() {
|
|
|
88
83
|
}
|
|
89
84
|
|
|
90
85
|
.id-video {
|
|
91
|
-
width:
|
|
86
|
+
width: 99%;
|
|
92
87
|
text-align: center;
|
|
93
88
|
position: relative;
|
|
94
89
|
overflow: hidden;
|
|
95
90
|
}
|
|
96
|
-
|
|
97
|
-
position: absolute;
|
|
98
|
-
border-style: solid;
|
|
99
|
-
border-color: rgba(0, 0, 0, 0.48);
|
|
100
|
-
box-sizing: border-box;
|
|
101
|
-
inset: -1px;
|
|
102
|
-
}
|
|
103
|
-
|
|
91
|
+
|
|
104
92
|
.id-video-container {
|
|
105
93
|
margin: auto;
|
|
106
94
|
padding: 0px;
|
|
107
95
|
}
|
|
108
96
|
}
|
|
109
|
-
|
|
97
|
+
.id-video-container {
|
|
98
|
+
display: flex;
|
|
99
|
+
flex-direction: column;
|
|
100
|
+
align-items: center;
|
|
101
|
+
}
|
|
110
102
|
.id-video {
|
|
111
103
|
width: 100%;
|
|
112
104
|
text-align: center;
|
|
@@ -129,6 +121,12 @@ function templateString() {
|
|
|
129
121
|
border-radius: 0.25rem;
|
|
130
122
|
inset: -1px;
|
|
131
123
|
}
|
|
124
|
+
canvas {
|
|
125
|
+
border-width: 0.25rem;
|
|
126
|
+
border-color: #9394ab;
|
|
127
|
+
border-style: solid;
|
|
128
|
+
border-radius: 0.25rem;
|
|
129
|
+
}
|
|
132
130
|
|
|
133
131
|
.description {
|
|
134
132
|
align-self: center;
|
|
@@ -162,24 +160,25 @@ function templateString() {
|
|
|
162
160
|
padding-top: 10px;
|
|
163
161
|
}
|
|
164
162
|
</style>
|
|
163
|
+
${styles(this.themeColor)}
|
|
165
164
|
<div id='document-capture-screen' class='flow center flex-column'>
|
|
166
|
-
<smileid-navigation ${this.showNavigation ? 'show-navigation' : ''} ${this.hideBack ? 'hide-back' : ''}></smileid-navigation>
|
|
167
|
-
<h2 class='text-base font-bold color
|
|
165
|
+
<smileid-navigation theme-color='${this.themeColor}' ${this.showNavigation ? 'show-navigation' : ''} ${this.hideBack ? 'hide-back' : ''}></smileid-navigation>
|
|
166
|
+
<h2 class='text-base font-bold title-color'>${this.documentName}</h2>
|
|
168
167
|
<div class="circle-progress" id="loader">
|
|
169
168
|
${this.cameraError ? '' : '<p class="spinner"></p>'}
|
|
170
169
|
${this.cameraError ? `<p style="--flow-space: 4rem" class='color-red | center'>${this.cameraError}</p>` : '<p style="--flow-space: 4rem">Checking permissions</p>'}
|
|
171
170
|
</div>
|
|
172
171
|
<div class='section | flow ${this.isPortraitCaptureView ? 'portrait' : 'landscape'}'>
|
|
173
172
|
<div class='id-video-container'>
|
|
174
|
-
<div class='id-video ${this.isPortraitCaptureView ? 'portrait' : 'landscape'}' >
|
|
173
|
+
<div class='id-video ${this.isPortraitCaptureView ? 'portrait' : 'landscape'}' hidden>
|
|
175
174
|
</div>
|
|
176
175
|
<div class='video-footer sticky'>
|
|
177
|
-
<h2 class='text-base font-bold color
|
|
178
|
-
<h4 class='text-base font-normal color
|
|
176
|
+
<h2 class='text-base font-bold title-color reset-margin-block id-side'>${this.title}</h2>
|
|
177
|
+
<h4 class='text-base font-normal title-color description reset-margin-block'>Make sure all corners are visible and there is no glare.</h4>
|
|
179
178
|
<div class='actions' hidden>
|
|
180
179
|
<button id='capture-id-image' class='button icon-btn | center' type='button'>
|
|
181
180
|
<svg xmlns="http://www.w3.org/2000/svg" width="70" height="70" viewBox="0 0 70 70" fill="none" aria-hidden="true" focusable="false">
|
|
182
|
-
<path fill-rule="evenodd" clip-rule="evenodd" d="M35 70C54.33 70 70 54.33 70 35C70 15.67 54.33 0 35 0C15.67 0 0 15.67 0 35C0 54.33 15.67 70 35 70ZM61 35C61 49.3594 49.3594 61 35 61C20.6406 61 9 49.3594 9 35C9 20.6406 20.6406 9 35 9C49.3594 9 61 20.6406 61 35ZM65 35C65 51.5685 51.5685 65 35 65C18.4315 65 5 51.5685 5 35C5 18.4315 18.4315 5 35 5C51.5685 5 65 18.4315 65 35Z" fill="
|
|
181
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M35 70C54.33 70 70 54.33 70 35C70 15.67 54.33 0 35 0C15.67 0 0 15.67 0 35C0 54.33 15.67 70 35 70ZM61 35C61 49.3594 49.3594 61 35 61C20.6406 61 9 49.3594 9 35C9 20.6406 20.6406 9 35 9C49.3594 9 61 20.6406 61 35ZM65 35C65 51.5685 51.5685 65 35 65C18.4315 65 5 51.5685 5 35C5 18.4315 18.4315 5 35 5C51.5685 5 65 18.4315 65 35Z" fill="${this.themeColor}"/>
|
|
183
182
|
</svg>
|
|
184
183
|
<span class='visually-hidden'>Capture Document</span>
|
|
185
184
|
</button>
|
|
@@ -192,7 +191,8 @@ function templateString() {
|
|
|
192
191
|
`;
|
|
193
192
|
}
|
|
194
193
|
|
|
195
|
-
const
|
|
194
|
+
const documentCaptureScale = 0.6;
|
|
195
|
+
|
|
196
196
|
class DocumentCapture extends HTMLElement {
|
|
197
197
|
constructor() {
|
|
198
198
|
super();
|
|
@@ -223,10 +223,14 @@ class DocumentCapture extends HTMLElement {
|
|
|
223
223
|
try {
|
|
224
224
|
await SmartCamera.getMedia({
|
|
225
225
|
audio: false,
|
|
226
|
-
video:
|
|
226
|
+
video: {
|
|
227
|
+
...SmartCamera.environmentOptions,
|
|
228
|
+
aspectRatio: { ideal: 16 / 9 },
|
|
229
|
+
},
|
|
227
230
|
});
|
|
228
231
|
} catch (error) {
|
|
229
232
|
console.error(error.constraint);
|
|
233
|
+
console.error(error.message);
|
|
230
234
|
}
|
|
231
235
|
|
|
232
236
|
this.handleIDStream(SmartCamera.stream);
|
|
@@ -249,49 +253,16 @@ class DocumentCapture extends HTMLElement {
|
|
|
249
253
|
const canvas = document.createElement('canvas');
|
|
250
254
|
if (this.isPortraitCaptureView) {
|
|
251
255
|
canvas.width = video.videoWidth;
|
|
252
|
-
canvas.height =
|
|
253
|
-
|
|
254
|
-
// Draw the video frame onto the canvas
|
|
255
|
-
const ctx = canvas.getContext('2d');
|
|
256
|
-
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
257
|
-
|
|
258
|
-
// Get the dimensions of the video preview frame
|
|
259
|
-
const previewWidth = PORTRAIT_ID_PREVIEW_WIDTH;
|
|
260
|
-
const previewHeight = PORTRAIT_ID_PREVIEW_HEIGHT;
|
|
261
|
-
|
|
262
|
-
// Define the padding value
|
|
263
|
-
const paddingPercent = 1.1; // 110% of the preview dimensions because we previously scaled the image, old value was 50%;
|
|
264
|
-
const paddedWidth = previewWidth * (1 + paddingPercent);
|
|
265
|
-
const paddedHeight = previewHeight * (1 + paddingPercent);
|
|
266
|
-
|
|
267
|
-
// Calculate the dimensions of the cropped image based on the padded preview frame dimensions
|
|
268
|
-
const cropWidth = paddedWidth;
|
|
269
|
-
const cropHeight = paddedHeight;
|
|
270
|
-
const cropLeft = (canvas.width - cropWidth) / 2;
|
|
271
|
-
const cropTop = (canvas.height - cropHeight) / 2;
|
|
272
|
-
|
|
273
|
-
// Create a new canvas element for the cropped image
|
|
274
|
-
const croppedCanvas = document.createElement('canvas');
|
|
275
|
-
croppedCanvas.width = cropWidth;
|
|
276
|
-
croppedCanvas.height = cropHeight;
|
|
277
|
-
|
|
278
|
-
// Draw the cropped image onto the new canvas
|
|
279
|
-
const croppedCtx = croppedCanvas.getContext('2d');
|
|
280
|
-
croppedCtx.drawImage(
|
|
281
|
-
canvas,
|
|
282
|
-
cropLeft,
|
|
283
|
-
cropTop,
|
|
284
|
-
cropWidth,
|
|
285
|
-
cropHeight,
|
|
286
|
-
0,
|
|
287
|
-
0,
|
|
288
|
-
cropWidth,
|
|
289
|
-
cropHeight,
|
|
290
|
-
);
|
|
256
|
+
canvas.height = (canvas.width * 16) / 9;
|
|
291
257
|
|
|
292
|
-
const
|
|
293
|
-
|
|
258
|
+
const previewCanvas = document.createElement('canvas');
|
|
259
|
+
previewCanvas.width = canvas.width;
|
|
260
|
+
previewCanvas.height = canvas.height;
|
|
294
261
|
|
|
262
|
+
this.updatePortraitId(canvas, video, 1, 1);
|
|
263
|
+
this.updatePortraitId(previewCanvas, video);
|
|
264
|
+
const image = canvas.toDataURL('image/jpeg');
|
|
265
|
+
const previewImage = previewCanvas.toDataURL('image/jpeg');
|
|
295
266
|
return {
|
|
296
267
|
image,
|
|
297
268
|
originalHeight: canvas.height,
|
|
@@ -304,66 +275,33 @@ class DocumentCapture extends HTMLElement {
|
|
|
304
275
|
canvas.width = 2240;
|
|
305
276
|
canvas.height = 1260;
|
|
306
277
|
|
|
307
|
-
const context = canvas.getContext('2d');
|
|
308
|
-
|
|
309
|
-
const { aspectRatio } = this._calculateVideoOffset(video);
|
|
310
|
-
|
|
311
|
-
if (aspectRatio < 1) {
|
|
312
|
-
canvas.width = video.videoWidth;
|
|
313
|
-
canvas.height = video.videoHeight;
|
|
314
|
-
|
|
315
|
-
// Draw the video frame onto the canvas
|
|
316
|
-
const ctx = canvas.getContext('2d');
|
|
317
|
-
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
318
|
-
|
|
319
|
-
const paddedWidth = canvas.width;
|
|
320
|
-
const paddedHeight = canvas.width / fixedAspectRatio;
|
|
321
|
-
|
|
322
|
-
// Calculate the dimensions of the cropped image based on the padded preview frame dimensions
|
|
323
|
-
const cropWidth = paddedWidth;
|
|
324
|
-
const cropHeight = paddedHeight;
|
|
325
|
-
const cropLeft = 0;
|
|
326
|
-
const cropTop = canvas.height / 2 - paddedHeight / 2;
|
|
327
|
-
|
|
328
|
-
// Create a new canvas element for the cropped image
|
|
329
|
-
const croppedCanvas = document.createElement('canvas');
|
|
330
|
-
croppedCanvas.width = cropWidth;
|
|
331
|
-
croppedCanvas.height = cropHeight;
|
|
332
|
-
|
|
333
|
-
// Draw the cropped image onto the new canvas
|
|
334
|
-
const croppedCtx = croppedCanvas.getContext('2d');
|
|
335
|
-
croppedCtx.drawImage(
|
|
336
|
-
canvas,
|
|
337
|
-
cropLeft,
|
|
338
|
-
cropTop,
|
|
339
|
-
cropWidth,
|
|
340
|
-
cropHeight,
|
|
341
|
-
0,
|
|
342
|
-
0,
|
|
343
|
-
cropWidth,
|
|
344
|
-
cropHeight,
|
|
345
|
-
);
|
|
346
|
-
const image = croppedCanvas.toDataURL('image/jpeg');
|
|
347
|
-
|
|
348
|
-
return {
|
|
349
|
-
image,
|
|
350
|
-
originalHeight: canvas.height,
|
|
351
|
-
originalWidth: canvas.width,
|
|
352
|
-
previewImage: image,
|
|
353
|
-
...this.idCardRegion,
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
|
|
357
278
|
const height = canvas.width / (video.videoWidth / video.videoHeight);
|
|
358
279
|
canvas.height = height;
|
|
359
|
-
|
|
280
|
+
|
|
281
|
+
const previewCanvas = document.createElement('canvas');
|
|
282
|
+
previewCanvas.height = canvas.height;
|
|
283
|
+
previewCanvas.width = canvas.width;
|
|
284
|
+
const isPortrait = video.videoWidth < video.videoHeight;
|
|
285
|
+
if (isPortrait) {
|
|
286
|
+
const intermediateCanvas = document.createElement('canvas');
|
|
287
|
+
previewCanvas.height = canvas.width / 1.75;
|
|
288
|
+
canvas.width = 2240;
|
|
289
|
+
canvas.height = canvas.width / 1.77;
|
|
290
|
+
this._capturePortraitToLandscapeImage(intermediateCanvas, video);
|
|
291
|
+
this._drawLandscapeImageFromCanvas(canvas, intermediateCanvas, 1, 1);
|
|
292
|
+
this._drawLandscapeImageFromCanvas(previewCanvas, intermediateCanvas);
|
|
293
|
+
} else {
|
|
294
|
+
this._drawLandscapeImage(canvas, video, 1, 1);
|
|
295
|
+
this._drawLandscapeImage(previewCanvas, video);
|
|
296
|
+
}
|
|
360
297
|
const image = canvas.toDataURL('image/jpeg');
|
|
361
298
|
|
|
299
|
+
const previewImage = previewCanvas.toDataURL('image/jpeg');
|
|
362
300
|
return {
|
|
363
301
|
image,
|
|
364
302
|
originalHeight: canvas.height,
|
|
365
303
|
originalWidth: canvas.width,
|
|
366
|
-
previewImage
|
|
304
|
+
previewImage,
|
|
367
305
|
...this.idCardRegion,
|
|
368
306
|
};
|
|
369
307
|
}
|
|
@@ -401,28 +339,19 @@ class DocumentCapture extends HTMLElement {
|
|
|
401
339
|
}
|
|
402
340
|
|
|
403
341
|
handleIDStream(stream) {
|
|
404
|
-
const videoExists = this.shadowRoot.querySelector('
|
|
405
|
-
let video = null;
|
|
342
|
+
const videoExists = this.shadowRoot.querySelector('canvas');
|
|
406
343
|
if (videoExists) {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
video = document.createElement('video');
|
|
344
|
+
// remove canvas
|
|
345
|
+
videoExists.remove();
|
|
410
346
|
}
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
video.style.width = `${width}px`;
|
|
418
|
-
video.style.height = '0px';
|
|
419
|
-
video.style.display = 'block';
|
|
347
|
+
let video = null;
|
|
348
|
+
let canvas = null;
|
|
349
|
+
video = document.createElement('video');
|
|
350
|
+
canvas = document.createElement('canvas');
|
|
351
|
+
const videoContainer = this.shadowRoot.querySelector('.id-video-container');
|
|
352
|
+
|
|
420
353
|
video.muted = true;
|
|
421
354
|
video.setAttribute('muted', 'true');
|
|
422
|
-
if (this.isPortraitCaptureView) {
|
|
423
|
-
video.style.objectFit = 'cover';
|
|
424
|
-
video.style.scale = '1';
|
|
425
|
-
}
|
|
426
355
|
|
|
427
356
|
video.autoplay = true;
|
|
428
357
|
video.playsInline = true;
|
|
@@ -432,104 +361,245 @@ class DocumentCapture extends HTMLElement {
|
|
|
432
361
|
video.src = window.URL.createObjectURL(stream);
|
|
433
362
|
}
|
|
434
363
|
|
|
364
|
+
canvas.width = videoContainer.clientWidth;
|
|
365
|
+
canvas.height = (videoContainer.clientWidth * 9) / 16;
|
|
366
|
+
if (this.isPortraitCaptureView) {
|
|
367
|
+
canvas.height = (videoContainer.clientWidth * 16) / 9;
|
|
368
|
+
}
|
|
369
|
+
|
|
435
370
|
video.onloadedmetadata = () => {
|
|
436
371
|
video.play();
|
|
372
|
+
|
|
373
|
+
this.shadowRoot.querySelector('#loader').hidden = true;
|
|
374
|
+
this.shadowRoot.querySelector('.id-video').hidden = false;
|
|
375
|
+
this.shadowRoot.querySelector('.actions').hidden = false;
|
|
376
|
+
if (!videoExists) {
|
|
377
|
+
videoContainer.prepend(canvas);
|
|
378
|
+
}
|
|
437
379
|
};
|
|
438
380
|
|
|
439
381
|
const onVideoStart = () => {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
offsetWidth,
|
|
444
|
-
videoHeight,
|
|
445
|
-
videoWidth,
|
|
446
|
-
} = this._calculateVideoOffset(video);
|
|
382
|
+
if (video.paused || video.ended) return;
|
|
383
|
+
video.removeEventListener('playing', onVideoStart);
|
|
384
|
+
const aspectRatio = video.videoWidth / video.videoHeight;
|
|
447
385
|
const portrait = aspectRatio < 1;
|
|
386
|
+
if (this.isPortraitCaptureView) {
|
|
387
|
+
this.updatePortraitId(canvas, video);
|
|
388
|
+
requestAnimationFrame(onVideoStart);
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
448
391
|
|
|
449
392
|
if (portrait) {
|
|
450
393
|
videoContainer.classList.add('mobile-camera-screen');
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
const idCardRegionWidth = videoWidth - offsetWidth;
|
|
457
|
-
const idCardRegionHeight = videoHeight - offsetHeight;
|
|
458
|
-
|
|
459
|
-
const rightLeftBorderSize = 20;
|
|
460
|
-
const topBottomBorderSize = 20;
|
|
461
|
-
this.idCardRegion = {
|
|
462
|
-
height: idCardRegionHeight,
|
|
463
|
-
rightLeftBorderSize,
|
|
464
|
-
topBottomBorderSize,
|
|
465
|
-
width: idCardRegionWidth,
|
|
466
|
-
x: offsetWidth / 2,
|
|
467
|
-
y: offsetHeight / 2,
|
|
468
|
-
};
|
|
469
|
-
|
|
470
|
-
let videoOverlay = videoContainer.querySelector('.video-overlay');
|
|
471
|
-
if (videoOverlay) {
|
|
472
|
-
videoOverlay.remove();
|
|
394
|
+
const intermediateCanvas = document.createElement('canvas');
|
|
395
|
+
this._capturePortraitToLandscapeImage(intermediateCanvas, video);
|
|
396
|
+
this._drawLandscapeImageFromCanvas(canvas, intermediateCanvas);
|
|
397
|
+
} else {
|
|
398
|
+
this._drawLandscapeImage(canvas, video);
|
|
473
399
|
}
|
|
474
|
-
|
|
475
|
-
const shadeColor = 'white';
|
|
476
|
-
videoOverlay.classList.add('video-overlay');
|
|
477
|
-
|
|
478
|
-
videoOverlay.style.borderLeft = `${rightLeftBorderSize}px solid ${shadeColor}`;
|
|
479
|
-
videoOverlay.style.borderRight = `${rightLeftBorderSize}px solid ${shadeColor}`;
|
|
480
|
-
videoOverlay.style.borderTop = `${topBottomBorderSize}px solid ${shadeColor}`;
|
|
481
|
-
videoOverlay.style.borderBottom = `${topBottomBorderSize}px solid ${shadeColor}`;
|
|
482
|
-
videoOverlay.style.top = '0px';
|
|
483
|
-
videoOverlay.style.bottom = '0px';
|
|
484
|
-
videoOverlay.style.left = '0px';
|
|
485
|
-
videoOverlay.style.right = '0px';
|
|
486
|
-
videoOverlay.style.inset = '-1px';
|
|
487
|
-
|
|
488
|
-
const innerBorder = document.createElement('div');
|
|
489
|
-
innerBorder.classList.add('inner-border');
|
|
490
|
-
videoOverlay.appendChild(innerBorder);
|
|
491
|
-
videoContainer.appendChild(videoOverlay);
|
|
492
|
-
this.videoOverlay = videoOverlay;
|
|
493
|
-
this.shadowRoot.querySelector('#loader').hidden = true;
|
|
494
|
-
this.shadowRoot.querySelector('.id-video').hidden = false;
|
|
495
|
-
this.shadowRoot.querySelector('.actions').hidden = false;
|
|
496
|
-
video.removeEventListener('playing', onVideoStart);
|
|
400
|
+
requestAnimationFrame(onVideoStart);
|
|
497
401
|
};
|
|
498
402
|
|
|
499
403
|
video.addEventListener('playing', onVideoStart);
|
|
500
404
|
|
|
501
|
-
if (!videoExists) {
|
|
502
|
-
videoContainer.prepend(video);
|
|
503
|
-
}
|
|
504
|
-
|
|
505
405
|
this._IDStream = stream;
|
|
506
406
|
this._IDVideo = video;
|
|
507
407
|
}
|
|
508
408
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
const
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
const
|
|
519
|
-
|
|
409
|
+
_drawLandscapeImage(
|
|
410
|
+
canvas,
|
|
411
|
+
video = this._IDVideo,
|
|
412
|
+
scaleHeight = documentCaptureScale,
|
|
413
|
+
scaleWidth = documentCaptureScale,
|
|
414
|
+
) {
|
|
415
|
+
const heightScaleFactor = this.height
|
|
416
|
+
? this.height / video.videoHeight
|
|
417
|
+
: scaleHeight;
|
|
418
|
+
const widthScaleFactor = this.width
|
|
419
|
+
? this.width / video.videoWidth
|
|
420
|
+
: scaleWidth;
|
|
421
|
+
const scaleHeightOffset = (1 - scaleHeight) / 2;
|
|
422
|
+
const scaleWidthOffset = (1 - scaleWidth) / 2;
|
|
423
|
+
const width = video.videoWidth * widthScaleFactor;
|
|
424
|
+
const height = video.videoHeight * heightScaleFactor;
|
|
425
|
+
const startX = video.videoWidth * scaleWidthOffset;
|
|
426
|
+
const startY = video.videoHeight * scaleHeightOffset;
|
|
427
|
+
|
|
428
|
+
canvas
|
|
429
|
+
.getContext('2d')
|
|
430
|
+
.drawImage(
|
|
431
|
+
video,
|
|
432
|
+
startX,
|
|
433
|
+
startY,
|
|
434
|
+
width,
|
|
435
|
+
height,
|
|
436
|
+
0,
|
|
437
|
+
0,
|
|
438
|
+
canvas.width,
|
|
439
|
+
canvas.height,
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
_capturePortraitToLandscapeImage(canvas, video = this._IDVideo) {
|
|
444
|
+
const { videoHeight, videoWidth } = video;
|
|
445
|
+
const cropWidth = videoWidth;
|
|
446
|
+
const cropHeight = (videoWidth * 9) / 16; // convert to landscape aspect ratio
|
|
447
|
+
const startX = 0;
|
|
448
|
+
const startY = (videoHeight - cropHeight) / 2;
|
|
449
|
+
|
|
450
|
+
canvas.width = cropWidth;
|
|
451
|
+
canvas.height = cropHeight;
|
|
452
|
+
|
|
453
|
+
canvas
|
|
454
|
+
.getContext('2d')
|
|
455
|
+
.drawImage(
|
|
456
|
+
video,
|
|
457
|
+
startX,
|
|
458
|
+
startY,
|
|
459
|
+
cropWidth,
|
|
460
|
+
cropHeight,
|
|
461
|
+
0,
|
|
462
|
+
0,
|
|
463
|
+
canvas.width,
|
|
464
|
+
canvas.height,
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
_drawLandscapeImageFromCanvas(
|
|
469
|
+
canvas,
|
|
470
|
+
sourceCanvas,
|
|
471
|
+
scaleHeight = documentCaptureScale,
|
|
472
|
+
scaleWidth = documentCaptureScale,
|
|
473
|
+
) {
|
|
474
|
+
const heightScaleFactor = this.height
|
|
475
|
+
? this.height / sourceCanvas.height
|
|
476
|
+
: scaleHeight;
|
|
477
|
+
const widthScaleFactor = this.width
|
|
478
|
+
? this.width / sourceCanvas.width
|
|
479
|
+
: scaleWidth;
|
|
480
|
+
const scaleHeightOffset = (1 - scaleHeight) / 2;
|
|
481
|
+
const scaleWidthOffset = (1 - scaleWidth) / 2;
|
|
482
|
+
const width = sourceCanvas.width * widthScaleFactor;
|
|
483
|
+
const height = sourceCanvas.height * heightScaleFactor;
|
|
484
|
+
const startX = sourceCanvas.width * scaleWidthOffset;
|
|
485
|
+
const startY = sourceCanvas.height * scaleHeightOffset;
|
|
486
|
+
|
|
487
|
+
canvas
|
|
488
|
+
.getContext('2d')
|
|
489
|
+
.drawImage(
|
|
490
|
+
sourceCanvas,
|
|
491
|
+
startX,
|
|
492
|
+
startY,
|
|
493
|
+
width,
|
|
494
|
+
height,
|
|
495
|
+
0,
|
|
496
|
+
0,
|
|
497
|
+
canvas.width,
|
|
498
|
+
canvas.height,
|
|
499
|
+
);
|
|
500
|
+
}
|
|
520
501
|
|
|
521
|
-
|
|
522
|
-
const
|
|
502
|
+
_drawPortraitToLandscapeImage(canvas, video = this._IDVideo) {
|
|
503
|
+
const { videoHeight, videoWidth } = video;
|
|
504
|
+
const cropWidth = 600;
|
|
505
|
+
const cropHeight = 400;
|
|
523
506
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
507
|
+
canvas.width = cropWidth;
|
|
508
|
+
canvas.height = cropHeight;
|
|
509
|
+
|
|
510
|
+
const startX = (videoWidth - cropWidth) / 2;
|
|
511
|
+
const startY = (videoHeight - cropHeight) / 2;
|
|
512
|
+
|
|
513
|
+
canvas
|
|
514
|
+
.getContext('2d')
|
|
515
|
+
.drawImage(
|
|
516
|
+
video,
|
|
517
|
+
startX,
|
|
518
|
+
startY,
|
|
519
|
+
cropWidth,
|
|
520
|
+
cropHeight,
|
|
521
|
+
0,
|
|
522
|
+
0,
|
|
523
|
+
canvas.width,
|
|
524
|
+
canvas.height,
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
updatePortraitId(
|
|
529
|
+
destinationCanvas,
|
|
530
|
+
video = this._IDVideo,
|
|
531
|
+
scaleHeight = documentCaptureScale,
|
|
532
|
+
scaleWidth = documentCaptureScale,
|
|
533
|
+
) {
|
|
534
|
+
const { videoWidth, videoHeight } = video;
|
|
535
|
+
|
|
536
|
+
if (videoWidth && videoHeight) {
|
|
537
|
+
const intermediateCanvas = document.createElement('canvas');
|
|
538
|
+
const aspectRatio = 9 / 16;
|
|
539
|
+
let cropWidth;
|
|
540
|
+
let cropHeight;
|
|
541
|
+
let offsetX;
|
|
542
|
+
let offsetY;
|
|
543
|
+
|
|
544
|
+
if (videoWidth / videoHeight > aspectRatio) {
|
|
545
|
+
// we scale the canvas to portrait aspect ratio
|
|
546
|
+
cropHeight = videoHeight;
|
|
547
|
+
cropWidth = cropHeight * aspectRatio;
|
|
548
|
+
offsetX = (videoWidth - cropWidth) / 2;
|
|
549
|
+
offsetY = 0;
|
|
550
|
+
} else {
|
|
551
|
+
// video already has portrait aspect ratio
|
|
552
|
+
cropWidth = videoWidth;
|
|
553
|
+
cropHeight = cropWidth;
|
|
554
|
+
offsetX = 0;
|
|
555
|
+
offsetY = 0;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
intermediateCanvas.height = cropHeight;
|
|
559
|
+
intermediateCanvas.width = cropWidth;
|
|
560
|
+
// draw the video frame onto the intermediate canvas
|
|
561
|
+
intermediateCanvas
|
|
562
|
+
.getContext('2d')
|
|
563
|
+
.drawImage(
|
|
564
|
+
video,
|
|
565
|
+
offsetX,
|
|
566
|
+
offsetY,
|
|
567
|
+
cropWidth,
|
|
568
|
+
cropHeight,
|
|
569
|
+
0,
|
|
570
|
+
0,
|
|
571
|
+
intermediateCanvas.width,
|
|
572
|
+
intermediateCanvas.height,
|
|
573
|
+
);
|
|
574
|
+
|
|
575
|
+
// draw the intermediate canvas onto the destination canvas
|
|
576
|
+
// we scale image based on the scaleHeight and scaleWidth
|
|
577
|
+
const heightScaleFactor = this.height
|
|
578
|
+
? this.height / cropWidth
|
|
579
|
+
: scaleHeight;
|
|
580
|
+
const widthScaleFactor = this.width
|
|
581
|
+
? this.width / cropHeight
|
|
582
|
+
: scaleWidth;
|
|
583
|
+
const scaleHeightOffset = (1 - scaleHeight) / 2;
|
|
584
|
+
const scaleWidthOffset = (1 - scaleWidth) / 2;
|
|
585
|
+
const width = cropWidth * widthScaleFactor;
|
|
586
|
+
const height = cropHeight * heightScaleFactor;
|
|
587
|
+
const startX = cropWidth * scaleWidthOffset;
|
|
588
|
+
const startY = cropHeight * scaleHeightOffset;
|
|
589
|
+
destinationCanvas
|
|
590
|
+
.getContext('2d')
|
|
591
|
+
.drawImage(
|
|
592
|
+
intermediateCanvas,
|
|
593
|
+
startX,
|
|
594
|
+
startY,
|
|
595
|
+
width,
|
|
596
|
+
height,
|
|
597
|
+
0,
|
|
598
|
+
0,
|
|
599
|
+
destinationCanvas.width,
|
|
600
|
+
destinationCanvas.height,
|
|
601
|
+
);
|
|
602
|
+
}
|
|
533
603
|
}
|
|
534
604
|
|
|
535
605
|
_stopIDVideoStream(stream = this._IDStream) {
|
|
@@ -568,7 +638,7 @@ class DocumentCapture extends HTMLElement {
|
|
|
568
638
|
}
|
|
569
639
|
|
|
570
640
|
get themeColor() {
|
|
571
|
-
return this.getAttribute('theme-color') || '#
|
|
641
|
+
return this.getAttribute('theme-color') || '#001096';
|
|
572
642
|
}
|
|
573
643
|
|
|
574
644
|
get hideAttribution() {
|
|
@@ -591,6 +661,14 @@ class DocumentCapture extends HTMLElement {
|
|
|
591
661
|
);
|
|
592
662
|
}
|
|
593
663
|
|
|
664
|
+
get height() {
|
|
665
|
+
return this.getAttribute('height');
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
get width() {
|
|
669
|
+
return this.getAttribute('width');
|
|
670
|
+
}
|
|
671
|
+
|
|
594
672
|
get hidden() {
|
|
595
673
|
return this.getAttribute('hidden');
|
|
596
674
|
}
|
|
@@ -2,14 +2,21 @@ import SmartCamera from '../../../../domain/camera/src/SmartCamera';
|
|
|
2
2
|
import './index';
|
|
3
3
|
|
|
4
4
|
const meta = {
|
|
5
|
+
args: {
|
|
6
|
+
'theme-color': '#001096',
|
|
7
|
+
},
|
|
8
|
+
argTypes: {
|
|
9
|
+
'theme-color': { control: 'color' },
|
|
10
|
+
},
|
|
5
11
|
component: 'document-capture',
|
|
6
|
-
render: (
|
|
12
|
+
render: (args) => `
|
|
7
13
|
<document-capture
|
|
8
14
|
show-navigation
|
|
9
15
|
document-capture-modes="camera,upload"
|
|
10
16
|
document-name="Driver's License"
|
|
11
17
|
side-of-id="Front"
|
|
12
|
-
document-type="${documentType}"
|
|
18
|
+
document-type="${args.documentType}"
|
|
19
|
+
theme-color='${args['theme-color']}'
|
|
13
20
|
>
|
|
14
21
|
</document-capture>
|
|
15
22
|
`,
|