@privyid/persona 0.3.0 → 0.4.0
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/dist/components/camera/Camera.vue +366 -0
- package/dist/components/camera/Camera.vue.d.ts +92 -0
- package/dist/components/camera/adapter/adapter.d.ts +41 -0
- package/dist/components/camera/adapter/adapter.mjs +3 -0
- package/dist/components/camera/adapter/capture.d.ts +5 -0
- package/dist/components/camera/adapter/capture.mjs +12 -0
- package/dist/components/camera/adapter/liveness.d.ts +5 -0
- package/dist/components/camera/adapter/liveness.mjs +46 -0
- package/dist/components/camera/adapter/qr-code.d.ts +2 -0
- package/dist/components/camera/adapter/qr-code.mjs +19 -0
- package/dist/components/camera/assets/shutter.wav +0 -0
- package/dist/components/camera/utils/motion.d.ts +29 -0
- package/dist/components/camera/utils/motion.mjs +88 -0
- package/dist/components/camera/utils/take-picture.d.ts +1 -0
- package/dist/components/camera/utils/take-picture.mjs +12 -0
- package/dist/components/collapse/Collapse.vue +88 -0
- package/dist/components/collapse/Collapse.vue.d.ts +29 -0
- package/dist/components/contextual-bar/ContextualBar.vue +292 -0
- package/dist/components/contextual-bar/ContextualBar.vue.d.ts +77 -0
- package/dist/components/contextual-bar/assets/images/img-background-contextualbar.svg +14 -0
- package/dist/components/contextual-bar/assets/images/img-bg-contextualbar.svg +19 -0
- package/dist/components/cropper/Cropper.vue +418 -0
- package/dist/components/cropper/Cropper.vue.d.ts +148 -0
- package/dist/components/cropper/assets/ps-neutral.png +0 -0
- package/dist/components/cropper/assets/sample-1.jpg +0 -0
- package/dist/components/cropper/utils/crop-image.d.ts +44 -0
- package/dist/components/cropper/utils/crop-image.mjs +43 -0
- package/dist/components/cropper/utils/use-pinch.d.ts +8 -0
- package/dist/components/cropper/utils/use-pinch.mjs +42 -0
- package/dist/components/cropper/utils/use-ratio.d.ts +9 -0
- package/dist/components/cropper/utils/use-ratio.mjs +24 -0
- package/dist/components/dialog/Dialog.vue +2 -0
- package/dist/components/dialog/DialogFooter.vue +4 -2
- package/dist/components/dialog/use-dialog.d.ts +3 -0
- package/dist/components/dropdown/Dropdown.vue.d.ts +2 -2
- package/dist/components/dropzone/Dropzone.vue +166 -0
- package/dist/components/dropzone/Dropzone.vue.d.ts +68 -0
- package/dist/components/dropzone/utils/accept.d.ts +1 -0
- package/dist/components/dropzone/utils/accept.mjs +17 -0
- package/dist/components/input/Input.vue +1 -1
- package/dist/components/modal/Modal.vue +62 -2
- package/dist/components/modal/Modal.vue.d.ts +21 -0
- package/dist/components/nav/Nav.vue +7 -1
- package/dist/components/nav/Nav.vue.d.ts +2 -2
- package/dist/components/nav/NavForm.vue +19 -0
- package/dist/components/nav/NavItem.vue +10 -0
- package/dist/components/nav/NavItemDropdown.vue +5 -1
- package/dist/components/nav/NavItemDropdown.vue.d.ts +9 -0
- package/dist/components/navbar/Navbar.vue +143 -0
- package/dist/components/navbar/Navbar.vue.d.ts +56 -0
- package/dist/components/navbar/NavbarBrand.vue +33 -0
- package/dist/components/navbar/NavbarBrand.vue.d.ts +14 -0
- package/dist/components/navbar/NavbarNav.vue +113 -0
- package/dist/components/navbar/NavbarNav.vue.d.ts +18 -0
- package/dist/components/navbar/NavbarToggle.vue +75 -0
- package/dist/components/navbar/NavbarToggle.vue.d.ts +6 -0
- package/dist/components/navbar/use-navbar.d.ts +10 -0
- package/dist/components/navbar/use-navbar.mjs +1 -0
- package/dist/components/navbar-menu/NavbarNavMenu.vue +62 -0
- package/dist/components/navbar-menu/NavbarNavMenu.vue.d.ts +15 -0
- package/dist/components/progress/Progress.vue +200 -0
- package/dist/components/progress/Progress.vue.d.ts +42 -0
- package/dist/components/progress/ProgressItem.vue +50 -0
- package/dist/components/progress/ProgressItem.vue.d.ts +25 -0
- package/dist/components/select/Select.vue.d.ts +1 -1
- package/dist/components/sidebar/SidebarNav.vue +4 -0
- package/dist/components/signature-draw/SignatureDrawDesktop.vue +2 -2
- package/dist/components/signature-draw/utils/canvas.d.ts +7 -1
- package/dist/components/signature-draw/utils/canvas.mjs +8 -2
- package/dist/components/signature-draw/utils/use-draw.mjs +6 -5
- package/dist/components/signature-text/SignatureText.vue.d.ts +1 -1
- package/dist/components/signature-text/utils/formatter.mjs +1 -1
- package/dist/components/spread/Spread.vue +55 -0
- package/dist/components/spread/Spread.vue.d.ts +14 -0
- package/dist/components/steps/Step.vue +55 -0
- package/dist/components/steps/Step.vue.d.ts +32 -0
- package/dist/components/steps/StepSlider.vue +39 -0
- package/dist/components/steps/StepSlider.vue.d.ts +25 -0
- package/dist/components/steps/Steps.vue +110 -0
- package/dist/components/steps/Steps.vue.d.ts +56 -0
- package/dist/components/steps/use-steps.d.ts +25 -0
- package/dist/components/steps/use-steps.mjs +52 -0
- package/dist/components/steps/utils/hook.d.ts +4 -0
- package/dist/components/steps/utils/hook.mjs +16 -0
- package/dist/components/subheading/Subheading.vue +2 -2
- package/dist/components/tabs/TabContent.vue +2 -2
- package/dist/components/tabs/Tabs.vue +4 -5
- package/dist/components/tabs/Tabs.vue.d.ts +1 -1
- package/dist/components/textarea/Textarea.vue.d.ts +1 -1
- package/dist/components/utils/base64.d.ts +8 -0
- package/dist/components/utils/base64.mjs +29 -0
- package/dist/components/utils/color.d.ts +6 -1
- package/dist/components/utils/color.mjs +4 -0
- package/dist/components/utils/vnode.d.ts +14 -1
- package/dist/components/utils/vnode.mjs +5 -2
- package/dist/components/wizard/Wizard.vue +85 -0
- package/dist/components/wizard/Wizard.vue.d.ts +72 -0
- package/dist/components/wizard/WizardBody.vue +50 -0
- package/dist/components/wizard/WizardBody.vue.d.ts +23 -0
- package/dist/components/wizard/WizardHeader.vue +38 -0
- package/dist/components/wizard/WizardHeader.vue.d.ts +16 -0
- package/dist/components/wizard/WizardStep.vue +20 -0
- package/dist/components/wizard/WizardStep.vue.d.ts +34 -0
- package/dist/core/index.cjs +1 -75
- package/dist/core/index.d.ts +6 -1
- package/dist/core/index.mjs +15 -1
- package/dist/module.d.ts +8 -2
- package/dist/module.json +1 -1
- package/dist/module.mjs +14 -2
- package/dist/types.d.ts +5 -1
- package/package.json +11 -7
- package/dist/components/dialog/index.d.ts +0 -6
- package/dist/components/dialog/index.mjs +0 -5
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="camera"
|
|
4
|
+
data-testid="camera"
|
|
5
|
+
:data-deviceid="deviceId"
|
|
6
|
+
:class="classNames">
|
|
7
|
+
<video
|
|
8
|
+
v-if="stream"
|
|
9
|
+
ref="video"
|
|
10
|
+
data-testid="camera-video"
|
|
11
|
+
:srcObject.prop="stream"
|
|
12
|
+
class="camera__video"
|
|
13
|
+
muted
|
|
14
|
+
autoplay
|
|
15
|
+
playsinline
|
|
16
|
+
@play="onStart" />
|
|
17
|
+
|
|
18
|
+
<!-- Result -->
|
|
19
|
+
<img
|
|
20
|
+
v-if="isTaken && !stream"
|
|
21
|
+
data-testid="camera-result"
|
|
22
|
+
class="camera__result"
|
|
23
|
+
:src="preview"
|
|
24
|
+
alt="camera-result">
|
|
25
|
+
|
|
26
|
+
<!-- Camera off -->
|
|
27
|
+
<span
|
|
28
|
+
v-if="!isActive && !isTaken"
|
|
29
|
+
data-testid="camera-off"
|
|
30
|
+
class="camera__off-info">
|
|
31
|
+
Camera is off
|
|
32
|
+
</span>
|
|
33
|
+
|
|
34
|
+
<!-- Mask -->
|
|
35
|
+
<div class="camera__mask-container">
|
|
36
|
+
<div
|
|
37
|
+
v-if="!isTaken"
|
|
38
|
+
data-testid="camera-mask"
|
|
39
|
+
class="camera__mask" />
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<!-- Toast -->
|
|
43
|
+
<transition name="slide-up">
|
|
44
|
+
<div
|
|
45
|
+
v-if="message"
|
|
46
|
+
:key="message"
|
|
47
|
+
class="camera__toast">
|
|
48
|
+
<div
|
|
49
|
+
data-testid="camera-toast"
|
|
50
|
+
class="camera__toast-text">
|
|
51
|
+
{{ message }}
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</transition>
|
|
55
|
+
|
|
56
|
+
<!-- Control Button -->
|
|
57
|
+
<div class="camera__controls">
|
|
58
|
+
<p-button
|
|
59
|
+
v-if="cameras.length > 1 && !isTaken"
|
|
60
|
+
data-testid="camera-toggle"
|
|
61
|
+
color="secondary"
|
|
62
|
+
size="sm"
|
|
63
|
+
icon
|
|
64
|
+
pill
|
|
65
|
+
@click="toggle">
|
|
66
|
+
<icon-rotate />
|
|
67
|
+
</p-button>
|
|
68
|
+
|
|
69
|
+
<!-- Main Button -->
|
|
70
|
+
<p-button
|
|
71
|
+
v-if="!isActive && !isTaken"
|
|
72
|
+
data-testid="camera-turn-on"
|
|
73
|
+
class="camera__main-control"
|
|
74
|
+
icon
|
|
75
|
+
pill
|
|
76
|
+
@click="turnOn()">
|
|
77
|
+
<icon-camera />
|
|
78
|
+
</p-button>
|
|
79
|
+
<p-button
|
|
80
|
+
v-else-if="isActive && !isTaken"
|
|
81
|
+
data-testid="camera-take"
|
|
82
|
+
class="camera__main-control"
|
|
83
|
+
icon
|
|
84
|
+
pill
|
|
85
|
+
:disabled="isProcessing"
|
|
86
|
+
@click="take()">
|
|
87
|
+
<icon-camera />
|
|
88
|
+
</p-button>
|
|
89
|
+
<p-button
|
|
90
|
+
v-else
|
|
91
|
+
data-testid="camera-retake"
|
|
92
|
+
class="camera__main-control"
|
|
93
|
+
icon
|
|
94
|
+
pill
|
|
95
|
+
@click="retake()">
|
|
96
|
+
<icon-retake />
|
|
97
|
+
</p-button>
|
|
98
|
+
<!-- End Main Button -->
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
<slot
|
|
102
|
+
:cameras="cameras"
|
|
103
|
+
:preview="preview"
|
|
104
|
+
:stream="stream"
|
|
105
|
+
:video="video"
|
|
106
|
+
:toast="toast" />
|
|
107
|
+
</div>
|
|
108
|
+
</template>
|
|
109
|
+
|
|
110
|
+
<script>
|
|
111
|
+
import {
|
|
112
|
+
computed,
|
|
113
|
+
defineComponent,
|
|
114
|
+
onBeforeUnmount,
|
|
115
|
+
onMounted,
|
|
116
|
+
ref,
|
|
117
|
+
toRef
|
|
118
|
+
} from "vue-demi";
|
|
119
|
+
import pButton from "../button/Button.vue";
|
|
120
|
+
import IconRotate from "@carbon/icons-vue/lib/renew/16";
|
|
121
|
+
import IconCamera from "@carbon/icons-vue/lib/camera/24";
|
|
122
|
+
import IconRetake from "@carbon/icons-vue/lib/reset/24";
|
|
123
|
+
import shutterWav from "./assets/shutter.wav";
|
|
124
|
+
import { useSound } from "@vueuse/sound";
|
|
125
|
+
import { useVModel } from "../input/use-input";
|
|
126
|
+
import CaptureAdapter from "./adapter/capture";
|
|
127
|
+
import {
|
|
128
|
+
usePermission,
|
|
129
|
+
useDevicesList,
|
|
130
|
+
useUserMedia,
|
|
131
|
+
until
|
|
132
|
+
} from "@vueuse/core";
|
|
133
|
+
import * as dialog from "../dialog/use-dialog";
|
|
134
|
+
import defu from "defu";
|
|
135
|
+
export default defineComponent({
|
|
136
|
+
components: {
|
|
137
|
+
pButton,
|
|
138
|
+
IconCamera,
|
|
139
|
+
IconRetake,
|
|
140
|
+
IconRotate
|
|
141
|
+
},
|
|
142
|
+
props: {
|
|
143
|
+
modelValue: {
|
|
144
|
+
type: [
|
|
145
|
+
globalThis.File,
|
|
146
|
+
String,
|
|
147
|
+
Array
|
|
148
|
+
],
|
|
149
|
+
default: ""
|
|
150
|
+
},
|
|
151
|
+
modelModifiers: {
|
|
152
|
+
type: Object,
|
|
153
|
+
default: () => ({})
|
|
154
|
+
},
|
|
155
|
+
mirror: {
|
|
156
|
+
type: [Boolean, String],
|
|
157
|
+
default: void 0
|
|
158
|
+
},
|
|
159
|
+
mask: {
|
|
160
|
+
type: String,
|
|
161
|
+
default: void 0
|
|
162
|
+
},
|
|
163
|
+
adapter: {
|
|
164
|
+
type: Object,
|
|
165
|
+
default: CaptureAdapter
|
|
166
|
+
},
|
|
167
|
+
facingMode: {
|
|
168
|
+
type: String,
|
|
169
|
+
default: void 0
|
|
170
|
+
},
|
|
171
|
+
silent: {
|
|
172
|
+
type: Boolean,
|
|
173
|
+
default: false
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
models: {
|
|
177
|
+
prop: "modelValue",
|
|
178
|
+
event: "update:modelValue"
|
|
179
|
+
},
|
|
180
|
+
emits: [
|
|
181
|
+
"start",
|
|
182
|
+
"change",
|
|
183
|
+
"result",
|
|
184
|
+
"update:modelValue"
|
|
185
|
+
],
|
|
186
|
+
setup(props, { emit }) {
|
|
187
|
+
const model = useVModel(props);
|
|
188
|
+
const modifier = toRef(props, "modelModifiers");
|
|
189
|
+
const isProcessing = ref(false);
|
|
190
|
+
const isTaken = ref(false);
|
|
191
|
+
const preview = ref("");
|
|
192
|
+
const message = ref("");
|
|
193
|
+
const shutter = useSound(shutterWav);
|
|
194
|
+
const video = ref();
|
|
195
|
+
const permission = usePermission("camera", { controls: true });
|
|
196
|
+
const camera = ref(0);
|
|
197
|
+
const meta = computed(() => {
|
|
198
|
+
return defu({
|
|
199
|
+
mirror: props.mirror,
|
|
200
|
+
mask: props.mask,
|
|
201
|
+
facingMode: props.facingMode
|
|
202
|
+
}, props.adapter.meta, { autoStart: false });
|
|
203
|
+
});
|
|
204
|
+
const { videoInputs: cameras } = useDevicesList({ constraints: { video: { facingMode: meta.value.facingMode } } });
|
|
205
|
+
const deviceId = computed(() => {
|
|
206
|
+
return cameras.value?.at(camera.value)?.deviceId;
|
|
207
|
+
});
|
|
208
|
+
const { stream, start, stop, enabled: isActive } = useUserMedia({
|
|
209
|
+
videoDeviceId: deviceId,
|
|
210
|
+
audioDeviceId: false
|
|
211
|
+
});
|
|
212
|
+
const classNames = computed(() => {
|
|
213
|
+
const result = [];
|
|
214
|
+
if (meta.value.mirror)
|
|
215
|
+
result.push("camera--mirror");
|
|
216
|
+
if (meta.value.mask)
|
|
217
|
+
result.push(`camera__mask--${meta.value.mask}`);
|
|
218
|
+
else
|
|
219
|
+
result.push("camera__mask--none");
|
|
220
|
+
return result;
|
|
221
|
+
});
|
|
222
|
+
async function turnOn() {
|
|
223
|
+
if (permission.state.value === "denied") {
|
|
224
|
+
await dialog.alert({
|
|
225
|
+
title: "Camera Access Blocked",
|
|
226
|
+
text: "Privy need to access your internal camera to process this journey",
|
|
227
|
+
confirm: { text: "Ok" },
|
|
228
|
+
footerAlign: "end"
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
if (permission.state.value === "prompt") {
|
|
232
|
+
await dialog.alert({
|
|
233
|
+
title: "Camera Access Required",
|
|
234
|
+
text: "Privy need to access your internal camera to process this journey",
|
|
235
|
+
confirm: { text: "Ok" },
|
|
236
|
+
footerAlign: "end"
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
await start();
|
|
240
|
+
}
|
|
241
|
+
function toggle() {
|
|
242
|
+
camera.value = (camera.value + 1) % cameras.value.length;
|
|
243
|
+
}
|
|
244
|
+
function toast(text) {
|
|
245
|
+
message.value = text;
|
|
246
|
+
}
|
|
247
|
+
async function take() {
|
|
248
|
+
isProcessing.value = true;
|
|
249
|
+
const output = await props.adapter.run({
|
|
250
|
+
video,
|
|
251
|
+
toast,
|
|
252
|
+
stream,
|
|
253
|
+
meta,
|
|
254
|
+
modifier
|
|
255
|
+
});
|
|
256
|
+
preview.value = output.preview;
|
|
257
|
+
model.value = output.result;
|
|
258
|
+
isTaken.value = true;
|
|
259
|
+
isProcessing.value = false;
|
|
260
|
+
isActive.value = false;
|
|
261
|
+
emit("result", output.result);
|
|
262
|
+
emit("change", output.result);
|
|
263
|
+
if (!props.silent)
|
|
264
|
+
shutter.play();
|
|
265
|
+
}
|
|
266
|
+
async function retake() {
|
|
267
|
+
isActive.value = true;
|
|
268
|
+
isTaken.value = false;
|
|
269
|
+
preview.value = "";
|
|
270
|
+
message.value = "";
|
|
271
|
+
}
|
|
272
|
+
function onStart() {
|
|
273
|
+
emit("start", stream.value);
|
|
274
|
+
if (meta.value.autoStart)
|
|
275
|
+
take();
|
|
276
|
+
}
|
|
277
|
+
onMounted(async () => {
|
|
278
|
+
if (permission.isSupported) {
|
|
279
|
+
await until(permission.state).not.toBeUndefined();
|
|
280
|
+
await turnOn();
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
onBeforeUnmount(() => {
|
|
284
|
+
stop();
|
|
285
|
+
});
|
|
286
|
+
return {
|
|
287
|
+
classNames,
|
|
288
|
+
cameras,
|
|
289
|
+
video,
|
|
290
|
+
stream,
|
|
291
|
+
toggle,
|
|
292
|
+
isActive,
|
|
293
|
+
isTaken,
|
|
294
|
+
isProcessing,
|
|
295
|
+
message,
|
|
296
|
+
take,
|
|
297
|
+
retake,
|
|
298
|
+
preview,
|
|
299
|
+
turnOn,
|
|
300
|
+
onStart,
|
|
301
|
+
toast,
|
|
302
|
+
deviceId
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
</script>
|
|
307
|
+
|
|
308
|
+
<style lang="postcss">
|
|
309
|
+
.camera {
|
|
310
|
+
@apply bg-black w-full flex flex-col aspect-video select-none relative;
|
|
311
|
+
|
|
312
|
+
&__video {
|
|
313
|
+
@apply flex-grow min-h-full max-w-full h-auto;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
&--mirror {
|
|
317
|
+
.camera__video {
|
|
318
|
+
@apply -scale-x-100;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
&__mask-container {
|
|
323
|
+
@apply absolute top-0 left-0 right-0 bottom-0 w-full h-full overflow-hidden pointer-events-none;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
&__mask {
|
|
327
|
+
@apply absolute top-1/2 left-1/2 shadow-mask -translate-x-1/2 -translate-y-1/2;
|
|
328
|
+
|
|
329
|
+
&--none &-container {
|
|
330
|
+
@apply hidden;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
&--square & {
|
|
334
|
+
@apply aspect-square w-2/3 md:w-1/2;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
&--round & {
|
|
338
|
+
@apply aspect-square rounded-full w-1/2;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
&--card & {
|
|
342
|
+
@apply aspect-[85.60/53.98] w-2/3 rounded-md;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
&__result {
|
|
347
|
+
@apply max-w-full h-auto;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
&__off-info {
|
|
351
|
+
@apply absolute bottom-20 text-white left-0 right-0 text-center text-sm;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
&__controls {
|
|
355
|
+
@apply py-3 flex w-full flex-shrink-0 justify-center items-center absolute bottom-0 gap-3;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
&__toast {
|
|
359
|
+
@apply absolute bottom-20 left-0 right-0 text-center text-white px-4;
|
|
360
|
+
|
|
361
|
+
&-text {
|
|
362
|
+
@apply bg-black bg-opacity-80 px-4 py-1 text-sm rounded shadow-md inline-block max-w-full truncate;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
</style>
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { ComputedRef, PropType } from 'vue-demi';
|
|
2
|
+
import { Adapter, MaskVariant, ModelModifier } from './adapter/adapter';
|
|
3
|
+
declare const _default: import("vue-demi").DefineComponent<{
|
|
4
|
+
modelValue: {
|
|
5
|
+
type: PropType<string | string[] | File | File[]>;
|
|
6
|
+
default: string;
|
|
7
|
+
};
|
|
8
|
+
modelModifiers: {
|
|
9
|
+
type: PropType<ModelModifier>;
|
|
10
|
+
default: () => ModelModifier;
|
|
11
|
+
};
|
|
12
|
+
mirror: {
|
|
13
|
+
type: PropType<boolean | "all" | "preview">;
|
|
14
|
+
default: any;
|
|
15
|
+
};
|
|
16
|
+
mask: {
|
|
17
|
+
type: PropType<MaskVariant>;
|
|
18
|
+
default: any;
|
|
19
|
+
};
|
|
20
|
+
adapter: {
|
|
21
|
+
type: PropType<Adapter>;
|
|
22
|
+
default: Adapter;
|
|
23
|
+
};
|
|
24
|
+
facingMode: {
|
|
25
|
+
type: PropType<ConstrainDOMString>;
|
|
26
|
+
default: any;
|
|
27
|
+
};
|
|
28
|
+
silent: {
|
|
29
|
+
type: BooleanConstructor;
|
|
30
|
+
default: boolean;
|
|
31
|
+
};
|
|
32
|
+
}, {
|
|
33
|
+
classNames: ComputedRef<string[]>;
|
|
34
|
+
cameras: ComputedRef<MediaDeviceInfo[]>;
|
|
35
|
+
video: import("vue-demi").Ref<HTMLVideoElement>;
|
|
36
|
+
stream: import("vue-demi").Ref<MediaStream>;
|
|
37
|
+
toggle: () => void;
|
|
38
|
+
isActive: import("vue-demi").Ref<boolean>;
|
|
39
|
+
isTaken: import("vue-demi").Ref<boolean>;
|
|
40
|
+
isProcessing: import("vue-demi").Ref<boolean>;
|
|
41
|
+
message: import("vue-demi").Ref<string>;
|
|
42
|
+
take: () => Promise<void>;
|
|
43
|
+
retake: () => Promise<void>;
|
|
44
|
+
preview: import("vue-demi").Ref<string>;
|
|
45
|
+
turnOn: () => Promise<void>;
|
|
46
|
+
onStart: () => void;
|
|
47
|
+
toast: (text: string) => void;
|
|
48
|
+
deviceId: ComputedRef<string>;
|
|
49
|
+
}, unknown, {}, {}, import("vue-demi").ComponentOptionsMixin, import("vue-demi").ComponentOptionsMixin, ("change" | "update:modelValue" | "start" | "result")[], "change" | "start" | "update:modelValue" | "result", import("vue-demi").VNodeProps & import("vue-demi").AllowedComponentProps & import("vue-demi").ComponentCustomProps, Readonly<import("vue-demi").ExtractPropTypes<{
|
|
50
|
+
modelValue: {
|
|
51
|
+
type: PropType<string | string[] | File | File[]>;
|
|
52
|
+
default: string;
|
|
53
|
+
};
|
|
54
|
+
modelModifiers: {
|
|
55
|
+
type: PropType<ModelModifier>;
|
|
56
|
+
default: () => ModelModifier;
|
|
57
|
+
};
|
|
58
|
+
mirror: {
|
|
59
|
+
type: PropType<boolean | "all" | "preview">;
|
|
60
|
+
default: any;
|
|
61
|
+
};
|
|
62
|
+
mask: {
|
|
63
|
+
type: PropType<MaskVariant>;
|
|
64
|
+
default: any;
|
|
65
|
+
};
|
|
66
|
+
adapter: {
|
|
67
|
+
type: PropType<Adapter>;
|
|
68
|
+
default: Adapter;
|
|
69
|
+
};
|
|
70
|
+
facingMode: {
|
|
71
|
+
type: PropType<ConstrainDOMString>;
|
|
72
|
+
default: any;
|
|
73
|
+
};
|
|
74
|
+
silent: {
|
|
75
|
+
type: BooleanConstructor;
|
|
76
|
+
default: boolean;
|
|
77
|
+
};
|
|
78
|
+
}>> & {
|
|
79
|
+
onChange?: (...args: any[]) => any;
|
|
80
|
+
"onUpdate:modelValue"?: (...args: any[]) => any;
|
|
81
|
+
onStart?: (...args: any[]) => any;
|
|
82
|
+
onResult?: (...args: any[]) => any;
|
|
83
|
+
}, {
|
|
84
|
+
modelValue: string | string[] | File | File[];
|
|
85
|
+
mask: MaskVariant;
|
|
86
|
+
mirror: boolean | "all" | "preview";
|
|
87
|
+
facingMode: any;
|
|
88
|
+
modelModifiers: ModelModifier;
|
|
89
|
+
adapter: Adapter;
|
|
90
|
+
silent: boolean;
|
|
91
|
+
}>;
|
|
92
|
+
export default _default;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ComputedRef, Ref } from 'vue-demi';
|
|
2
|
+
export declare type MaskVariant = 'square' | 'round' | 'card' | 'none';
|
|
3
|
+
export declare type MirrorMode = boolean | 'preview' | 'all';
|
|
4
|
+
export interface ModelModifier {
|
|
5
|
+
base64: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface AdapterMeta {
|
|
8
|
+
/**
|
|
9
|
+
* Auto take an image,
|
|
10
|
+
* It will start processing when camera started.
|
|
11
|
+
*/
|
|
12
|
+
autoStart: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Default value for props `facingMode`
|
|
15
|
+
*/
|
|
16
|
+
mask: MaskVariant;
|
|
17
|
+
/**
|
|
18
|
+
* Default value for props `mirror`
|
|
19
|
+
*/
|
|
20
|
+
mirror: MirrorMode;
|
|
21
|
+
/**
|
|
22
|
+
* Default value for props `facingMode`
|
|
23
|
+
*/
|
|
24
|
+
facingMode: ConstrainDOMString;
|
|
25
|
+
}
|
|
26
|
+
export interface CameraContext {
|
|
27
|
+
video: Ref<HTMLVideoElement>;
|
|
28
|
+
stream: Ref<MediaStream>;
|
|
29
|
+
modifier: Ref<ModelModifier>;
|
|
30
|
+
meta: ComputedRef<AdapterMeta>;
|
|
31
|
+
toast: (message: string) => void;
|
|
32
|
+
}
|
|
33
|
+
export interface CameraResult {
|
|
34
|
+
preview: string;
|
|
35
|
+
result: string | string[] | globalThis.File | globalThis.File[];
|
|
36
|
+
}
|
|
37
|
+
export interface Adapter {
|
|
38
|
+
meta?: Partial<AdapterMeta>;
|
|
39
|
+
run: (context: CameraContext) => CameraResult | Promise<CameraResult>;
|
|
40
|
+
}
|
|
41
|
+
export declare function defineAdapter(adapter: Adapter): Adapter;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { fromBase64 } from "../../utils/base64.mjs";
|
|
2
|
+
import { takePicture } from "../utils/take-picture.mjs";
|
|
3
|
+
import { defineAdapter } from "./adapter.mjs";
|
|
4
|
+
export default defineAdapter({
|
|
5
|
+
meta: { facingMode: "environment" },
|
|
6
|
+
run({ video, meta, modifier }) {
|
|
7
|
+
const isMirrored = !!(meta.value.mirror && meta.value.mirror !== "preview");
|
|
8
|
+
const preview = takePicture(video.value, isMirrored);
|
|
9
|
+
const result = modifier.value.base64 ? preview : fromBase64(preview);
|
|
10
|
+
return { preview, result };
|
|
11
|
+
}
|
|
12
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { fromBase64 } from "../../utils/base64.mjs";
|
|
2
|
+
import {
|
|
3
|
+
createTemplate,
|
|
4
|
+
motionDetection,
|
|
5
|
+
MOTION_AREA_SIZE,
|
|
6
|
+
MOTION_FPS,
|
|
7
|
+
MOTION_THRESHOLD,
|
|
8
|
+
takeSample
|
|
9
|
+
} from "../utils/motion";
|
|
10
|
+
import { takePicture } from "../utils/take-picture.mjs";
|
|
11
|
+
import { defineAdapter } from "./adapter.mjs";
|
|
12
|
+
export default defineAdapter({
|
|
13
|
+
meta: {
|
|
14
|
+
mask: "round",
|
|
15
|
+
mirror: true,
|
|
16
|
+
facingMode: "user"
|
|
17
|
+
},
|
|
18
|
+
async run({ video, toast, meta, modifier }) {
|
|
19
|
+
return await new Promise((resolve) => {
|
|
20
|
+
const isMirrored = !!(meta.value.mirror && meta.value.mirror !== "preview");
|
|
21
|
+
const photoA = takePicture(video.value, isMirrored);
|
|
22
|
+
const canvas = document.createElement("canvas");
|
|
23
|
+
canvas.width = MOTION_AREA_SIZE;
|
|
24
|
+
canvas.height = MOTION_AREA_SIZE;
|
|
25
|
+
let template;
|
|
26
|
+
const interval = setInterval(function processFrame() {
|
|
27
|
+
if (video.value) {
|
|
28
|
+
const imageData = takeSample(canvas, video.value);
|
|
29
|
+
if (!template)
|
|
30
|
+
template = createTemplate(imageData);
|
|
31
|
+
else if (motionDetection(imageData, template) > MOTION_THRESHOLD) {
|
|
32
|
+
const photoB = takePicture(video.value, isMirrored);
|
|
33
|
+
const result = modifier.value.base64 ? [photoB, photoA] : [fromBase64(photoB), fromBase64(photoA)];
|
|
34
|
+
clearInterval(interval);
|
|
35
|
+
toast("");
|
|
36
|
+
resolve({
|
|
37
|
+
preview: photoB,
|
|
38
|
+
result
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}, 1e3 / MOTION_FPS);
|
|
43
|
+
toast("Move your head slowly. Your photo will be taken automatically");
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { defineAdapter } from "./adapter.mjs";
|
|
2
|
+
import { BrowserQRCodeReader } from "@zxing/browser";
|
|
3
|
+
import { takePicture } from "../utils/take-picture.mjs";
|
|
4
|
+
export default defineAdapter({
|
|
5
|
+
meta: {
|
|
6
|
+
mask: "square",
|
|
7
|
+
facingMode: "environment",
|
|
8
|
+
autoStart: true
|
|
9
|
+
},
|
|
10
|
+
async run({ video, toast, meta }) {
|
|
11
|
+
const isMirrored = !!(meta.value.mirror && meta.value.mirror !== "preview");
|
|
12
|
+
const reader = new BrowserQRCodeReader();
|
|
13
|
+
const result = await reader.decodeOnceFromVideoElement(video.value);
|
|
14
|
+
const image = takePicture(video.value, isMirrored);
|
|
15
|
+
const text = result.getText();
|
|
16
|
+
toast(text);
|
|
17
|
+
return { preview: image, result: text };
|
|
18
|
+
}
|
|
19
|
+
});
|
|
Binary file
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface MotionTemplate {
|
|
2
|
+
centerX: number;
|
|
3
|
+
centerY: number;
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
xPos: number;
|
|
7
|
+
yPos: number;
|
|
8
|
+
buffer: Uint8ClampedArray;
|
|
9
|
+
}
|
|
10
|
+
export declare const MOTION_THRESHOLD = 40;
|
|
11
|
+
export declare const MOTION_FPS = 15;
|
|
12
|
+
export declare const MOTION_AREA_SIZE = 160;
|
|
13
|
+
/**
|
|
14
|
+
* Capture and create sample image from HTML Video Element
|
|
15
|
+
* @param canvas Canvas 2d context
|
|
16
|
+
* @param video Html Video element
|
|
17
|
+
*/
|
|
18
|
+
export declare function takeSample(canvas: HTMLCanvasElement, video: HTMLVideoElement): ImageData;
|
|
19
|
+
/**
|
|
20
|
+
* create motion template
|
|
21
|
+
* @param imageData sample imagedata
|
|
22
|
+
*/
|
|
23
|
+
export declare function createTemplate(imageData: ImageData): MotionTemplate;
|
|
24
|
+
/**
|
|
25
|
+
* Compare image with Motion template and return the movement distance
|
|
26
|
+
* @param imageData new sample's imagedata
|
|
27
|
+
* @param template motion template to compare
|
|
28
|
+
*/
|
|
29
|
+
export declare function motionDetection(imageData: ImageData, template: MotionTemplate): number;
|