@feugene/granularity-media 0.2.0 → 0.3.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/chunks/{config-CZ4ZDG0_.js → config-DHLlFslI.js} +1 -1
- package/dist/chunks/outputSize-D2FU4CbB.js +23 -0
- package/dist/components/GrCameraCapture/chunks/GrCameraCapture-CoSuBEfS.js +182 -0
- package/dist/components/GrCameraCapture/index.js +3 -3
- package/dist/components/GrImageCrop/chunks/{GrImageCrop-Fo2qrX2n.js → GrImageCrop-g1StRUis.js} +85 -84
- package/dist/components/GrImageCrop/index.js +1 -1
- package/dist/granular-provider.js +1 -1
- package/dist/index.js +5 -5
- package/dist/types/components/GrCameraCapture/GrCameraCapture.vue.d.ts +17 -5
- package/dist/types/components/GrCameraCapture/cameraState.d.ts +0 -17
- package/dist/types/components/GrCameraCapture/grCameraCaptureStyles.d.ts +6 -2
- package/dist/types/components/GrCameraCapture/index.d.ts +2 -2
- package/dist/types/components/GrImageCrop/GrImageCrop.vue.d.ts +1 -1
- package/dist/types/internal/__tests__/outputSize.test.d.ts +1 -0
- package/dist/types/internal/outputSize.d.ts +26 -0
- package/package.json +1 -1
- package/dist/components/GrCameraCapture/chunks/GrCameraCapture-DJKFrVfw.js +0 -197
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as e } from "./classTokens-bAzTjLuQ.js";
|
|
2
2
|
import { defineGranularComponent as t } from "@feugene/unocss-preset-granular/contract";
|
|
3
3
|
//#region src/components/GrCameraCapture/grCameraCaptureStyles.ts
|
|
4
|
-
var n = "grid gap-2", r = "relative w-full overflow-hidden rounded-[var(--gr-radius-md)] border border-[var(--gr-brd)] bg-[var(--gr-muted)]", i = "h-full w-full object-
|
|
4
|
+
var n = "grid gap-2", r = "relative w-full overflow-hidden rounded-[var(--gr-radius-md)] border border-[var(--gr-brd)] bg-[var(--gr-muted)]", i = "h-full w-full object-contain", a = "scale-x-[-1]", o = "absolute inset-0 grid place-content-center gap-3 p-4 text-center transition-opacity duration-[var(--gr-duration-base)] ease-[var(--gr-ease-out)]", s = "text-[var(--gr-muted-fg)]", c = "flex flex-wrap items-center gap-2", l = {
|
|
5
5
|
xs: "text-[length:var(--gr-control-text-xs)] leading-[var(--gr-control-leading-xs)]",
|
|
6
6
|
sm: "text-[length:var(--gr-control-text-sm)] leading-[var(--gr-control-leading-sm)]",
|
|
7
7
|
md: "text-[length:var(--gr-control-text-md)] leading-[var(--gr-control-leading-md)]",
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/internal/outputSize.ts
|
|
2
|
+
function e(e, t) {
|
|
3
|
+
let n = e.sh > 0 ? e.sw / e.sh : 1, r = (e) => Math.max(1, Math.round(e));
|
|
4
|
+
if (t?.width && t?.height) {
|
|
5
|
+
let n = Math.min(t.width / (e.sw || 1), t.height / (e.sh || 1));
|
|
6
|
+
return {
|
|
7
|
+
width: r(e.sw * n),
|
|
8
|
+
height: r(e.sh * n)
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
return t?.width ? {
|
|
12
|
+
width: r(t.width),
|
|
13
|
+
height: r(t.width / n)
|
|
14
|
+
} : t?.height ? {
|
|
15
|
+
width: r(t.height * n),
|
|
16
|
+
height: r(t.height)
|
|
17
|
+
} : {
|
|
18
|
+
width: r(e.sw),
|
|
19
|
+
height: r(e.sh)
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { e as t };
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { t as e } from "../../../chunks/outputSize-D2FU4CbB.js";
|
|
2
|
+
import { a as t, c as n, l as r, n as i, o as a, r as o, s, u as c } from "../../../chunks/config-DHLlFslI.js";
|
|
3
|
+
import { computed as l, createBlock as u, createCommentVNode as d, createElementBlock as f, createElementVNode as p, createTextVNode as m, createVNode as h, defineComponent as g, normalizeClass as _, normalizeStyle as v, onBeforeUnmount as y, onMounted as b, openBlock as x, ref as S, renderSlot as ee, shallowRef as C, toDisplayString as w, unref as T, withCtx as E } from "vue";
|
|
4
|
+
import D from "@feugene/granularity/components/GrButton";
|
|
5
|
+
import { useAnnouncer as O } from "@feugene/granularity/composables/useAnnouncer";
|
|
6
|
+
import { useGrComponentSize as te } from "@feugene/granularity/composables/useGrComponentConfig";
|
|
7
|
+
import { useGranularityTranslations as ne } from "@feugene/granularity/composables/useGranularityTranslations";
|
|
8
|
+
//#region src/components/GrCameraCapture/cameraState.ts
|
|
9
|
+
function k(e) {
|
|
10
|
+
switch (e instanceof Error ? e.name : "") {
|
|
11
|
+
case "NotAllowedError":
|
|
12
|
+
case "SecurityError":
|
|
13
|
+
case "PermissionDeniedError": return "denied";
|
|
14
|
+
case "NotFoundError":
|
|
15
|
+
case "DevicesNotFoundError":
|
|
16
|
+
case "OverconstrainedError": return "missing";
|
|
17
|
+
case "NotReadableError":
|
|
18
|
+
case "TrackStartError":
|
|
19
|
+
case "AbortError": return "busy";
|
|
20
|
+
default: return "error";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function A(e) {
|
|
24
|
+
return typeof e?.mediaDevices?.getUserMedia == "function";
|
|
25
|
+
}
|
|
26
|
+
function j(e, t) {
|
|
27
|
+
return t ?? e === "user";
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/components/GrCameraCapture/GrCameraCapture.vue?vue&type=script&setup=true&lang.ts
|
|
31
|
+
var M = ["aria-label"], N = /* @__PURE__ */ g({
|
|
32
|
+
__name: "GrCameraCapture",
|
|
33
|
+
props: {
|
|
34
|
+
facing: { default: "user" },
|
|
35
|
+
deviceId: { default: void 0 },
|
|
36
|
+
aspectRatio: { default: void 0 },
|
|
37
|
+
autoStart: {
|
|
38
|
+
type: Boolean,
|
|
39
|
+
default: !1
|
|
40
|
+
},
|
|
41
|
+
mirror: {
|
|
42
|
+
type: Boolean,
|
|
43
|
+
default: void 0
|
|
44
|
+
},
|
|
45
|
+
output: { default: void 0 },
|
|
46
|
+
size: { default: void 0 },
|
|
47
|
+
disabled: {
|
|
48
|
+
type: Boolean,
|
|
49
|
+
default: !1
|
|
50
|
+
},
|
|
51
|
+
ariaLabel: { default: void 0 }
|
|
52
|
+
},
|
|
53
|
+
emits: [
|
|
54
|
+
"capture",
|
|
55
|
+
"start",
|
|
56
|
+
"stop",
|
|
57
|
+
"statusChange"
|
|
58
|
+
],
|
|
59
|
+
setup(g, { expose: N, emit: P }) {
|
|
60
|
+
let F = g, I = P, { t: L } = ne(), { announce: R } = O(), z = te(() => F.size, { component: "GrCameraCapture" }), B = S(null), V = C(null), H = S("idle"), U = S(null), W = l(() => U.value ?? 4 / 3), G = l(() => j(F.facing, F.mirror)), K = l(() => H.value === "live"), q = l(() => {
|
|
61
|
+
switch (H.value) {
|
|
62
|
+
case "starting": return L("grMedia.camera.starting", "Requesting camera access…");
|
|
63
|
+
case "denied": return L("grMedia.camera.denied", "Camera access is blocked. Allow it in the browser settings for this site.");
|
|
64
|
+
case "missing": return L("grMedia.camera.missing", "No camera found on this device.");
|
|
65
|
+
case "busy": return L("grMedia.camera.busy", "The camera is busy in another application.");
|
|
66
|
+
case "insecure": return L("grMedia.camera.insecure", "The camera works only over HTTPS.");
|
|
67
|
+
case "error": return L("grMedia.camera.error", "The camera could not be started.");
|
|
68
|
+
default: return L("grMedia.camera.idle", "The camera is off.");
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
function J(e) {
|
|
72
|
+
H.value !== e && (H.value = e, I("statusChange", e));
|
|
73
|
+
}
|
|
74
|
+
function Y() {
|
|
75
|
+
let e = B.value;
|
|
76
|
+
!e || e.videoWidth <= 0 || e.videoHeight <= 0 || (U.value = e.videoWidth / e.videoHeight);
|
|
77
|
+
}
|
|
78
|
+
function X() {
|
|
79
|
+
V.value?.getTracks().forEach((e) => e.stop()), V.value = null, U.value = null, B.value && (B.value.srcObject = null);
|
|
80
|
+
}
|
|
81
|
+
async function Z() {
|
|
82
|
+
if (!(F.disabled || H.value === "starting" || H.value === "live")) {
|
|
83
|
+
if (!A(navigator)) {
|
|
84
|
+
J("insecure");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
J("starting");
|
|
88
|
+
try {
|
|
89
|
+
let e = await navigator.mediaDevices.getUserMedia({
|
|
90
|
+
video: {
|
|
91
|
+
...F.deviceId ? { deviceId: { exact: F.deviceId } } : { facingMode: F.facing },
|
|
92
|
+
...F.aspectRatio ? { aspectRatio: F.aspectRatio } : {}
|
|
93
|
+
},
|
|
94
|
+
audio: !1
|
|
95
|
+
});
|
|
96
|
+
V.value = e, B.value && (B.value.srcObject = e, B.value.addEventListener("loadedmetadata", Y, { once: !0 }), await B.value.play().catch(() => void 0), Y()), J("live"), I("start"), R(L("grMedia.camera.live", "Camera is on"));
|
|
97
|
+
} catch (e) {
|
|
98
|
+
X(), J(k(e)), R(q.value, { politeness: "assertive" });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function Q() {
|
|
103
|
+
V.value && (X(), J("idle"), I("stop"));
|
|
104
|
+
}
|
|
105
|
+
async function $() {
|
|
106
|
+
let t = B.value;
|
|
107
|
+
if (!t || !K.value) return null;
|
|
108
|
+
let n = {
|
|
109
|
+
sx: 0,
|
|
110
|
+
sy: 0,
|
|
111
|
+
sw: t.videoWidth,
|
|
112
|
+
sh: t.videoHeight
|
|
113
|
+
};
|
|
114
|
+
if (n.sw <= 0) return null;
|
|
115
|
+
let r = document.createElement("canvas"), i = e(n, F.output);
|
|
116
|
+
r.width = i.width, r.height = i.height;
|
|
117
|
+
let a = r.getContext("2d");
|
|
118
|
+
if (!a) return null;
|
|
119
|
+
a.drawImage(t, n.sx, n.sy, n.sw, n.sh, 0, 0, r.width, r.height);
|
|
120
|
+
let o = await new Promise((e) => {
|
|
121
|
+
r.toBlob(e, F.output?.type ?? "image/jpeg", F.output?.quality ?? .92);
|
|
122
|
+
});
|
|
123
|
+
return o && (I("capture", o), R(L("grMedia.camera.captured", "Photo taken"))), o;
|
|
124
|
+
}
|
|
125
|
+
return b(() => {
|
|
126
|
+
F.autoStart && Z();
|
|
127
|
+
}), y(X), N({
|
|
128
|
+
start: Z,
|
|
129
|
+
stop: Q,
|
|
130
|
+
capture: $,
|
|
131
|
+
status: H
|
|
132
|
+
}), (e, l) => (x(), f("div", {
|
|
133
|
+
class: _(T(a)),
|
|
134
|
+
role: "group",
|
|
135
|
+
"aria-label": g.ariaLabel ?? T(L)("grMedia.camera.label", "Camera")
|
|
136
|
+
}, [p("div", {
|
|
137
|
+
class: _(T(o)),
|
|
138
|
+
style: v({ aspectRatio: String(W.value) })
|
|
139
|
+
}, [p("video", {
|
|
140
|
+
ref_key: "videoEl",
|
|
141
|
+
ref: B,
|
|
142
|
+
class: _([T(c), G.value ? T(t) : ""]),
|
|
143
|
+
playsinline: "",
|
|
144
|
+
muted: "",
|
|
145
|
+
autoplay: ""
|
|
146
|
+
}, null, 2), K.value ? d("", !0) : (x(), f("div", {
|
|
147
|
+
key: 0,
|
|
148
|
+
class: _(T(n))
|
|
149
|
+
}, [p("p", { class: _([T(r), T(s)[T(z)]]) }, w(q.value), 3), H.value !== "starting" && H.value !== "insecure" && H.value !== "missing" ? (x(), u(T(D), {
|
|
150
|
+
key: 0,
|
|
151
|
+
size: T(z),
|
|
152
|
+
disabled: g.disabled,
|
|
153
|
+
onClick: Z
|
|
154
|
+
}, {
|
|
155
|
+
default: E(() => [m(w(H.value === "idle" ? T(L)("grMedia.camera.start", "Turn on camera") : T(L)("grMedia.camera.retry", "Try again")), 1)]),
|
|
156
|
+
_: 1
|
|
157
|
+
}, 8, ["size", "disabled"])) : d("", !0)], 2))], 6), p("div", { class: _(T(i)) }, [ee(e.$slots, "controls", {
|
|
158
|
+
status: H.value,
|
|
159
|
+
start: Z,
|
|
160
|
+
stop: Q,
|
|
161
|
+
capture: $
|
|
162
|
+
}, () => [h(T(D), {
|
|
163
|
+
size: T(z),
|
|
164
|
+
disabled: g.disabled || !K.value,
|
|
165
|
+
onClick: $
|
|
166
|
+
}, {
|
|
167
|
+
default: E(() => [m(w(T(L)("grMedia.camera.capture", "Take photo")), 1)]),
|
|
168
|
+
_: 1
|
|
169
|
+
}, 8, ["size", "disabled"]), K.value ? (x(), u(T(D), {
|
|
170
|
+
key: 0,
|
|
171
|
+
variant: "outline",
|
|
172
|
+
size: T(z),
|
|
173
|
+
disabled: g.disabled,
|
|
174
|
+
onClick: Q
|
|
175
|
+
}, {
|
|
176
|
+
default: E(() => [m(w(T(L)("grMedia.camera.stop", "Turn off")), 1)]),
|
|
177
|
+
_: 1
|
|
178
|
+
}, 8, ["size", "disabled"])) : d("", !0)])], 2)], 10, M));
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
//#endregion
|
|
182
|
+
export { j as i, k as n, A as r, N as t };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { i
|
|
3
|
-
export {
|
|
1
|
+
import { i as e, n as t, r as n, t as r } from "./chunks/GrCameraCapture-CoSuBEfS.js";
|
|
2
|
+
import { i, t as a } from "../../chunks/config-DHLlFslI.js";
|
|
3
|
+
export { r as GrCameraCapture, r as default, t as cameraStatusFromError, n as cameraSupported, a as grCameraCaptureConfig, i as grCameraCaptureSafelist, e as shouldMirrorPreview };
|
package/dist/components/GrImageCrop/chunks/{GrImageCrop-Fo2qrX2n.js → GrImageCrop-g1StRUis.js}
RENAMED
|
@@ -1,43 +1,44 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import
|
|
1
|
+
import { t as e } from "../../../chunks/outputSize-D2FU4CbB.js";
|
|
2
|
+
import { c as t, d as n, f as r, i, l as a, n as o, o as ee, r as te, s, u as ne } from "../../../chunks/config-Be_Mh801.js";
|
|
3
|
+
import { Fragment as c, computed as l, createCommentVNode as re, createElementBlock as u, createElementVNode as d, createVNode as ie, defineComponent as f, normalizeClass as p, normalizeStyle as m, onBeforeUnmount as ae, onMounted as oe, openBlock as h, ref as g, renderSlot as _, shallowRef as se, toDisplayString as v, unref as y, watch as ce } from "vue";
|
|
4
|
+
import { useGrComponentSize as le } from "@feugene/granularity/composables/useGrComponentConfig";
|
|
5
|
+
import { useGranularityTranslations as ue } from "@feugene/granularity/composables/useGranularityTranslations";
|
|
6
|
+
import b from "@feugene/granularity/components/GrSlider";
|
|
7
|
+
import { useDragGesture as de } from "@feugene/granularity/composables/useDragGesture";
|
|
7
8
|
//#region src/components/GrImageCrop/cropGeometry.ts
|
|
8
|
-
function
|
|
9
|
+
function x(e, t) {
|
|
9
10
|
return e.width <= 0 || e.height <= 0 ? 1 : Math.max(t.width / e.width, t.height / e.height);
|
|
10
11
|
}
|
|
11
|
-
function
|
|
12
|
-
let r =
|
|
12
|
+
function S(e, t, n) {
|
|
13
|
+
let r = x(e, t) * n;
|
|
13
14
|
return {
|
|
14
15
|
width: e.width * r,
|
|
15
16
|
height: e.height * r
|
|
16
17
|
};
|
|
17
18
|
}
|
|
18
|
-
function
|
|
19
|
-
let r =
|
|
19
|
+
function C(e, t, n) {
|
|
20
|
+
let r = S(e, t, n);
|
|
20
21
|
return {
|
|
21
22
|
x: Math.max(0, (r.width - t.width) / 2),
|
|
22
23
|
y: Math.max(0, (r.height - t.height) / 2)
|
|
23
24
|
};
|
|
24
25
|
}
|
|
25
|
-
function
|
|
26
|
-
let i =
|
|
26
|
+
function w(e, t, n, r) {
|
|
27
|
+
let i = C(t, n, r);
|
|
27
28
|
return {
|
|
28
29
|
x: Math.min(i.x, Math.max(-i.x, e.x)),
|
|
29
30
|
y: Math.min(i.y, Math.max(-i.y, e.y))
|
|
30
31
|
};
|
|
31
32
|
}
|
|
32
|
-
function
|
|
33
|
-
let i =
|
|
33
|
+
function T(e, t, n, r) {
|
|
34
|
+
let i = x(e, t) * n;
|
|
34
35
|
if (i <= 0) return {
|
|
35
36
|
sx: 0,
|
|
36
37
|
sy: 0,
|
|
37
38
|
sw: 0,
|
|
38
39
|
sh: 0
|
|
39
40
|
};
|
|
40
|
-
let a =
|
|
41
|
+
let a = w(r, e, t, n), o = S(e, t, n);
|
|
41
42
|
return {
|
|
42
43
|
sx: (o.width - t.width) / 2 / i - a.x / i,
|
|
43
44
|
sy: (o.height - t.height) / 2 / i - a.y / i,
|
|
@@ -45,7 +46,7 @@ function D(e, t, n, r) {
|
|
|
45
46
|
sh: t.height / i
|
|
46
47
|
};
|
|
47
48
|
}
|
|
48
|
-
function
|
|
49
|
+
function E(e, t) {
|
|
49
50
|
return {
|
|
50
51
|
width: e,
|
|
51
52
|
height: e / (t > 0 ? t : 1)
|
|
@@ -53,11 +54,11 @@ function O(e, t) {
|
|
|
53
54
|
}
|
|
54
55
|
//#endregion
|
|
55
56
|
//#region src/components/GrImageCrop/GrImageCrop.vue?vue&type=script&setup=true&lang.ts
|
|
56
|
-
var
|
|
57
|
+
var D = ["aria-label"], O = [
|
|
57
58
|
"tabindex",
|
|
58
59
|
"aria-label",
|
|
59
60
|
"aria-disabled"
|
|
60
|
-
],
|
|
61
|
+
], fe = ["src"], k = 12, A = .15, j = /* @__PURE__ */ f({
|
|
61
62
|
__name: "GrImageCrop",
|
|
62
63
|
props: {
|
|
63
64
|
src: { default: null },
|
|
@@ -79,14 +80,14 @@ var le = ["aria-label"], ue = [
|
|
|
79
80
|
"load",
|
|
80
81
|
"error"
|
|
81
82
|
],
|
|
82
|
-
setup(
|
|
83
|
-
let
|
|
83
|
+
setup(f, { expose: x, emit: S }) {
|
|
84
|
+
let C = f, j = S, { t: M } = ue(), N = le(() => C.size, { component: "GrImageCrop" }), P = g(null), F = se(null), I = g({
|
|
84
85
|
width: 0,
|
|
85
86
|
height: 0
|
|
86
|
-
}), L =
|
|
87
|
+
}), L = g(0), R = g({
|
|
87
88
|
x: 0,
|
|
88
89
|
y: 0
|
|
89
|
-
}), z =
|
|
90
|
+
}), z = g(null), B = l(() => E(L.value, C.aspectRatio)), V = l(() => F.value !== null && I.value.width > 0), H = l(() => Math.min(C.maxZoom, Math.max(1, C.zoom))), pe = l(() => {
|
|
90
91
|
let e = B.value.width > 0 && I.value.width > 0 ? Math.max(B.value.width / I.value.width, B.value.height / I.value.height) * H.value : 1;
|
|
91
92
|
return {
|
|
92
93
|
width: `${I.value.width}px`,
|
|
@@ -95,21 +96,21 @@ var le = ["aria-label"], ue = [
|
|
|
95
96
|
};
|
|
96
97
|
});
|
|
97
98
|
function U() {
|
|
98
|
-
return
|
|
99
|
+
return T(I.value, B.value, H.value, R.value);
|
|
99
100
|
}
|
|
100
101
|
function W() {
|
|
101
102
|
V.value && j("change", U());
|
|
102
103
|
}
|
|
103
104
|
function G(e, t) {
|
|
104
|
-
|
|
105
|
+
C.disabled || !V.value || (R.value = w({
|
|
105
106
|
x: R.value.x + e,
|
|
106
107
|
y: R.value.y + t
|
|
107
108
|
}, I.value, B.value, H.value), W());
|
|
108
109
|
}
|
|
109
110
|
function K(e) {
|
|
110
|
-
if (
|
|
111
|
-
let t = Math.min(
|
|
112
|
-
j("update:zoom", t), R.value =
|
|
111
|
+
if (C.disabled) return;
|
|
112
|
+
let t = Math.min(C.maxZoom, Math.max(1, e));
|
|
113
|
+
j("update:zoom", t), R.value = w(R.value, I.value, B.value, t), W();
|
|
113
114
|
}
|
|
114
115
|
function q() {
|
|
115
116
|
R.value = {
|
|
@@ -117,14 +118,14 @@ var le = ["aria-label"], ue = [
|
|
|
117
118
|
y: 0
|
|
118
119
|
}, j("update:zoom", 1), W();
|
|
119
120
|
}
|
|
120
|
-
let J =
|
|
121
|
+
let J = g({
|
|
121
122
|
x: 0,
|
|
122
123
|
y: 0
|
|
123
|
-
}), Y =
|
|
124
|
+
}), Y = g({
|
|
124
125
|
x: 0,
|
|
125
126
|
y: 0
|
|
126
|
-
}), X =
|
|
127
|
-
disabled: () =>
|
|
127
|
+
}), X = de({
|
|
128
|
+
disabled: () => C.disabled || !V.value,
|
|
128
129
|
onStart: (e) => {
|
|
129
130
|
J.value = {
|
|
130
131
|
x: e.clientX,
|
|
@@ -132,7 +133,7 @@ var le = ["aria-label"], ue = [
|
|
|
132
133
|
}, Y.value = { ...R.value };
|
|
133
134
|
},
|
|
134
135
|
onMove: (e) => {
|
|
135
|
-
R.value =
|
|
136
|
+
R.value = w({
|
|
136
137
|
x: Y.value.x + (e.clientX - J.value.x),
|
|
137
138
|
y: Y.value.y + (e.clientY - J.value.y)
|
|
138
139
|
}, I.value, B.value, H.value);
|
|
@@ -142,10 +143,10 @@ var le = ["aria-label"], ue = [
|
|
|
142
143
|
R.value = Y.value;
|
|
143
144
|
}
|
|
144
145
|
});
|
|
145
|
-
function pe(e) {
|
|
146
|
-
T.disabled || !V.value || (e.preventDefault(), K(H.value + (e.deltaY < 0 ? A : -.15)));
|
|
147
|
-
}
|
|
148
146
|
function me(e) {
|
|
147
|
+
C.disabled || !V.value || (e.preventDefault(), K(H.value + (e.deltaY < 0 ? A : -.15)));
|
|
148
|
+
}
|
|
149
|
+
function he(e) {
|
|
149
150
|
let t = {
|
|
150
151
|
ArrowLeft: () => G(k, 0),
|
|
151
152
|
ArrowRight: () => G(-12, 0),
|
|
@@ -161,7 +162,7 @@ var le = ["aria-label"], ue = [
|
|
|
161
162
|
function Z() {
|
|
162
163
|
z.value &&= (URL.revokeObjectURL(z.value), null);
|
|
163
164
|
}
|
|
164
|
-
function
|
|
165
|
+
function ge(e) {
|
|
165
166
|
if (!e) return null;
|
|
166
167
|
if (typeof e == "string") return e;
|
|
167
168
|
let t = URL.createObjectURL(e);
|
|
@@ -169,7 +170,7 @@ var le = ["aria-label"], ue = [
|
|
|
169
170
|
}
|
|
170
171
|
function Q(e) {
|
|
171
172
|
Z();
|
|
172
|
-
let t =
|
|
173
|
+
let t = ge(e);
|
|
173
174
|
if (!t) {
|
|
174
175
|
F.value = null, I.value = {
|
|
175
176
|
width: 0,
|
|
@@ -189,83 +190,83 @@ var le = ["aria-label"], ue = [
|
|
|
189
190
|
}), n.addEventListener("error", (e) => j("error", e)), n.src = t;
|
|
190
191
|
}
|
|
191
192
|
let $ = null;
|
|
192
|
-
|
|
193
|
+
oe(() => {
|
|
193
194
|
P.value && (L.value = P.value.clientWidth, $ = new ResizeObserver(([e]) => {
|
|
194
|
-
L.value = e?.contentRect.width ?? 0, R.value =
|
|
195
|
-
}), $.observe(P.value)), Q(
|
|
196
|
-
}),
|
|
195
|
+
L.value = e?.contentRect.width ?? 0, R.value = w(R.value, I.value, B.value, H.value);
|
|
196
|
+
}), $.observe(P.value)), Q(C.src);
|
|
197
|
+
}), ae(() => {
|
|
197
198
|
$?.disconnect(), $ = null, Z();
|
|
198
|
-
}),
|
|
199
|
-
async function
|
|
200
|
-
let
|
|
201
|
-
if (!
|
|
202
|
-
let
|
|
203
|
-
|
|
204
|
-
let
|
|
205
|
-
if (!
|
|
206
|
-
|
|
199
|
+
}), ce(() => C.src, (e) => Q(e));
|
|
200
|
+
async function _e() {
|
|
201
|
+
let t = F.value;
|
|
202
|
+
if (!t || !V.value) return null;
|
|
203
|
+
let n = U(), r = document.createElement("canvas"), i = e(n, C.output);
|
|
204
|
+
r.width = i.width, r.height = i.height;
|
|
205
|
+
let a = r.getContext("2d");
|
|
206
|
+
if (!a) return null;
|
|
207
|
+
a.drawImage(t, n.sx, n.sy, n.sw, n.sh, 0, 0, r.width, r.height);
|
|
207
208
|
try {
|
|
208
209
|
return await new Promise((e) => {
|
|
209
|
-
|
|
210
|
+
r.toBlob(e, C.output?.type ?? "image/png", C.output?.quality);
|
|
210
211
|
});
|
|
211
212
|
} catch (e) {
|
|
212
213
|
return typeof process < "u" && process.env.NODE_ENV !== "production" && console.warn("[granularity-media] GrImageCrop: экспорт кадра запрещён политикой источника. Картинка приехала с другого домена без заголовка `Access-Control-Allow-Origin`, и холст стал непригоден для чтения."), j("error", e), null;
|
|
213
214
|
}
|
|
214
215
|
}
|
|
215
|
-
return
|
|
216
|
-
crop:
|
|
216
|
+
return x({
|
|
217
|
+
crop: _e,
|
|
217
218
|
reset: q,
|
|
218
219
|
rect: U
|
|
219
|
-
}), (
|
|
220
|
-
class:
|
|
220
|
+
}), (e, l) => (h(), u("div", {
|
|
221
|
+
class: p(y(a)),
|
|
221
222
|
role: "group",
|
|
222
|
-
"aria-label":
|
|
223
|
-
}, [
|
|
223
|
+
"aria-label": f.ariaLabel ?? y(M)("grMedia.imageCrop.label", "Image crop")
|
|
224
|
+
}, [d("div", {
|
|
224
225
|
ref_key: "viewportEl",
|
|
225
226
|
ref: P,
|
|
226
227
|
role: "application",
|
|
227
|
-
class:
|
|
228
|
-
style:
|
|
229
|
-
tabindex:
|
|
230
|
-
"aria-label":
|
|
231
|
-
"aria-disabled":
|
|
232
|
-
onPointerdown:
|
|
233
|
-
onWheel:
|
|
234
|
-
onKeydown:
|
|
235
|
-
}, [V.value ? (
|
|
228
|
+
class: p([y(r), V.value ? "" : [y(i), y(ne)[y(N)]]]),
|
|
229
|
+
style: m(V.value ? { aspectRatio: String(f.aspectRatio) } : void 0),
|
|
230
|
+
tabindex: f.disabled || !V.value ? -1 : 0,
|
|
231
|
+
"aria-label": y(M)("grMedia.imageCrop.frame", "Frame: arrow keys move the image, plus and minus zoom"),
|
|
232
|
+
"aria-disabled": f.disabled || void 0,
|
|
233
|
+
onPointerdown: l[0] ||= (...e) => y(X).start && y(X).start(...e),
|
|
234
|
+
onWheel: me,
|
|
235
|
+
onKeydown: he
|
|
236
|
+
}, [V.value ? (h(), u(c, { key: 0 }, [d("img", {
|
|
236
237
|
src: F.value.src,
|
|
237
238
|
alt: "",
|
|
238
|
-
class:
|
|
239
|
-
style:
|
|
240
|
-
}, null, 14,
|
|
239
|
+
class: p([y(ee), y(X).isDragging.value ? "" : y(s)]),
|
|
240
|
+
style: m(pe.value)
|
|
241
|
+
}, null, 14, fe), f.shape === "circle" ? (h(), u("div", {
|
|
241
242
|
key: 0,
|
|
242
|
-
class:
|
|
243
|
-
}, null, 2)) : (
|
|
243
|
+
class: p(y(o))
|
|
244
|
+
}, null, 2)) : (h(), u("div", {
|
|
244
245
|
key: 1,
|
|
245
|
-
class:
|
|
246
|
-
}, null, 2))], 64)) :
|
|
246
|
+
class: p(y(t))
|
|
247
|
+
}, null, 2))], 64)) : _(e.$slots, "empty", {}, () => [d("span", { class: p(y(n)[y(N)]) }, v(y(M)("grMedia.imageCrop.empty", "No image selected")), 3)], void 0, 1)], 46, O), V.value ? (h(), u("div", {
|
|
247
248
|
key: 0,
|
|
248
|
-
class:
|
|
249
|
-
}, [
|
|
249
|
+
class: p(y(te))
|
|
250
|
+
}, [_(e.$slots, "controls", {
|
|
250
251
|
zoom: H.value,
|
|
251
252
|
setZoom: K
|
|
252
|
-
}, () => [
|
|
253
|
+
}, () => [ie(y(b), {
|
|
253
254
|
"model-value": H.value,
|
|
254
255
|
min: 1,
|
|
255
|
-
max:
|
|
256
|
+
max: f.maxZoom,
|
|
256
257
|
step: .01,
|
|
257
|
-
size:
|
|
258
|
-
disabled:
|
|
259
|
-
"aria-label":
|
|
260
|
-
"onUpdate:modelValue":
|
|
258
|
+
size: y(N),
|
|
259
|
+
disabled: f.disabled,
|
|
260
|
+
"aria-label": y(M)("grMedia.imageCrop.zoom", "Zoom"),
|
|
261
|
+
"onUpdate:modelValue": l[1] ||= (e) => K(Number(e))
|
|
261
262
|
}, null, 8, [
|
|
262
263
|
"model-value",
|
|
263
264
|
"max",
|
|
264
265
|
"size",
|
|
265
266
|
"disabled",
|
|
266
267
|
"aria-label"
|
|
267
|
-
])])], 2)) :
|
|
268
|
+
])])], 2)) : re("", !0)], 10, D));
|
|
268
269
|
}
|
|
269
270
|
});
|
|
270
271
|
//#endregion
|
|
271
|
-
export {
|
|
272
|
+
export { S as a, T as i, w as n, C as o, x as r, E as s, j as t };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as e, i as t, n, o as r, r as i, s as a, t as o } from "./chunks/GrImageCrop-
|
|
1
|
+
import { a as e, i as t, n, o as r, r as i, s as a, t as o } from "./chunks/GrImageCrop-g1StRUis.js";
|
|
2
2
|
import { a as s, t as c } from "../../chunks/config-Be_Mh801.js";
|
|
3
3
|
export { o as GrImageCrop, o as default, n as clampOffset, i as coverScale, t as cropRect, e as displaySize, c as grImageCropConfig, s as grImageCropSafelist, r as offsetBounds, a as viewportFor };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as e } from "./chunks/config-
|
|
1
|
+
import { t as e } from "./chunks/config-DHLlFslI.js";
|
|
2
2
|
import { t } from "./chunks/config-Be_Mh801.js";
|
|
3
3
|
import { defineGranularProvider as n, resolvePackageBaseUrl as r } from "@feugene/unocss-preset-granular/contract";
|
|
4
4
|
import { granularityProvider as i } from "@feugene/granularity/granular-provider";
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { i
|
|
3
|
-
import { a as
|
|
4
|
-
import { a as
|
|
5
|
-
export {
|
|
1
|
+
import { i as e, n as t, r as n, t as r } from "./components/GrCameraCapture/chunks/GrCameraCapture-CoSuBEfS.js";
|
|
2
|
+
import { i, t as a } from "./chunks/config-DHLlFslI.js";
|
|
3
|
+
import { a as o, i as s, n as c, o as l, r as u, s as d, t as f } from "./components/GrImageCrop/chunks/GrImageCrop-g1StRUis.js";
|
|
4
|
+
import { a as p, t as m } from "./chunks/config-Be_Mh801.js";
|
|
5
|
+
export { r as GrCameraCapture, f as GrImageCrop, t as cameraStatusFromError, n as cameraSupported, c as clampOffset, u as coverScale, s as cropRect, o as displaySize, a as grCameraCaptureConfig, i as grCameraCaptureSafelist, m as grImageCropConfig, p as grImageCropSafelist, l as offsetBounds, e as shouldMirrorPreview, d as viewportFor };
|
|
@@ -11,6 +11,14 @@ export interface GrCameraCaptureProps {
|
|
|
11
11
|
facing?: GrCameraFacing;
|
|
12
12
|
/** Конкретное устройство, если приложение его уже выбрало. */
|
|
13
13
|
deviceId?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Пожелание камере: какое соотношение сторон запросить у устройства.
|
|
16
|
+
*
|
|
17
|
+
* Это **не обрезка**. Камера, которая умеет запрошенное, отдаст его сама;
|
|
18
|
+
* та, что не умеет, отдаст своё — и показан, и снят будет её настоящий кадр.
|
|
19
|
+
* Нужен ровно квадрат — это `GrImageCrop` после съёмки.
|
|
20
|
+
*/
|
|
21
|
+
aspectRatio?: number;
|
|
14
22
|
/**
|
|
15
23
|
* Включать камеру сразу.
|
|
16
24
|
*
|
|
@@ -18,7 +26,6 @@ export interface GrCameraCaptureProps {
|
|
|
18
26
|
* отклоняют не глядя, а второй раз браузер уже не спросит.
|
|
19
27
|
*/
|
|
20
28
|
autoStart?: boolean;
|
|
21
|
-
aspectRatio?: number;
|
|
22
29
|
/** Зеркалить превью. По умолчанию — только фронтальную камеру. */
|
|
23
30
|
mirror?: boolean;
|
|
24
31
|
output?: GrCameraCaptureOutput;
|
|
@@ -44,7 +51,12 @@ type __VLS_Slots = {
|
|
|
44
51
|
declare function start(): Promise<void>;
|
|
45
52
|
declare function stop(): void;
|
|
46
53
|
/**
|
|
47
|
-
* Снимок текущего
|
|
54
|
+
* Снимок текущего кадра — целиком, без среза.
|
|
55
|
+
*
|
|
56
|
+
* Камеры на разных устройствах отдают разные размеры и соотношения, поэтому
|
|
57
|
+
* подгонять кадр под окно бессмысленно: на одном телефоне срезалось бы одно, на
|
|
58
|
+
* другом другое. Снимок повторяет то, что показывало превью, а обрезка под
|
|
59
|
+
* нужное место — работа `GrImageCrop`.
|
|
48
60
|
*
|
|
49
61
|
* Зеркало превью сюда **не переносится**: на снимке оказывается то, что было
|
|
50
62
|
* перед камерой. Иначе текст на визитке или в документе уехал бы в зазеркалье —
|
|
@@ -55,7 +67,7 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
|
|
|
55
67
|
facing: string;
|
|
56
68
|
deviceId: undefined;
|
|
57
69
|
autoStart: boolean;
|
|
58
|
-
aspectRatio:
|
|
70
|
+
aspectRatio: undefined;
|
|
59
71
|
mirror: undefined;
|
|
60
72
|
output: undefined;
|
|
61
73
|
size: undefined;
|
|
@@ -75,7 +87,7 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
|
|
|
75
87
|
facing: string;
|
|
76
88
|
deviceId: undefined;
|
|
77
89
|
autoStart: boolean;
|
|
78
|
-
aspectRatio:
|
|
90
|
+
aspectRatio: undefined;
|
|
79
91
|
mirror: undefined;
|
|
80
92
|
output: undefined;
|
|
81
93
|
size: undefined;
|
|
@@ -89,8 +101,8 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
|
|
|
89
101
|
}>, {
|
|
90
102
|
facing: GrCameraFacing;
|
|
91
103
|
deviceId: string;
|
|
92
|
-
autoStart: boolean;
|
|
93
104
|
aspectRatio: number;
|
|
105
|
+
autoStart: boolean;
|
|
94
106
|
mirror: boolean;
|
|
95
107
|
output: GrCameraCaptureOutput;
|
|
96
108
|
size: GrCameraCaptureSize;
|
|
@@ -48,20 +48,3 @@ export declare function cameraSupported(navigatorLike: Pick<Navigator, 'mediaDev
|
|
|
48
48
|
* уедет в зазеркалье — а снимают ими ровно это.
|
|
49
49
|
*/
|
|
50
50
|
export declare function shouldMirrorPreview(facing: 'user' | 'environment', mirror: boolean | undefined): boolean;
|
|
51
|
-
export interface GrCameraFrameRect {
|
|
52
|
-
sx: number;
|
|
53
|
-
sy: number;
|
|
54
|
-
sw: number;
|
|
55
|
-
sh: number;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Какая часть кадра камеры попадёт в снимок при заданном соотношении сторон.
|
|
59
|
-
*
|
|
60
|
-
* Камера отдаёт своё соотношение (обычно 4:3 или 16:9), а приложению нужно
|
|
61
|
-
* своё. Лишнее срезается по центру — так же, как это делает `object-fit: cover`
|
|
62
|
-
* в превью: снимок обязан совпасть с тем, что пользователь видел.
|
|
63
|
-
*/
|
|
64
|
-
export declare function cameraFrameRect(frame: {
|
|
65
|
-
width: number;
|
|
66
|
-
height: number;
|
|
67
|
-
}, aspectRatio: number): GrCameraFrameRect;
|
|
@@ -2,8 +2,12 @@ export type GrCameraCaptureSize = 'xs' | 'sm' | 'md' | 'lg';
|
|
|
2
2
|
export type GrCameraFacing = 'user' | 'environment';
|
|
3
3
|
export declare const rootClass = "grid gap-2";
|
|
4
4
|
export declare const frameClass = "relative w-full overflow-hidden rounded-[var(--gr-radius-md)] border border-[var(--gr-brd)] bg-[var(--gr-muted)]";
|
|
5
|
-
/**
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Превью **вписывается**, а не покрывает: кадр показывается целиком, в своих
|
|
7
|
+
* пропорциях. При совпадающей рамке разницы нет, но между стартом и первым
|
|
8
|
+
* кадром рамка ещё держит запасные 4:3 — и `cover` срезал бы картинку.
|
|
9
|
+
*/
|
|
10
|
+
export declare const videoClass = "h-full w-full object-contain";
|
|
7
11
|
export declare const mirroredClass = "scale-x-[-1]";
|
|
8
12
|
/**
|
|
9
13
|
* Подложка состояния поверх рамки. Появление плавное: отказ и ошибка приходят
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { default } from './GrCameraCapture.vue';
|
|
2
2
|
export { default as GrCameraCapture } from './GrCameraCapture.vue';
|
|
3
|
-
export type {
|
|
4
|
-
export {
|
|
3
|
+
export type { GrCameraStatus } from './cameraState';
|
|
4
|
+
export { cameraStatusFromError, cameraSupported, shouldMirrorPreview } from './cameraState';
|
|
5
5
|
export { grCameraCaptureConfig } from './config';
|
|
6
6
|
export type { GrCameraCaptureConfigurableProps } from './defaults';
|
|
7
7
|
export type { GrCameraCaptureEmits, GrCameraCaptureOutput, GrCameraCaptureProps } from './GrCameraCapture.vue';
|
|
@@ -85,12 +85,12 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
|
|
|
85
85
|
ariaLabel: undefined;
|
|
86
86
|
}>>> & Readonly<{
|
|
87
87
|
onError?: ((error: unknown) => any) | undefined;
|
|
88
|
-
"onUpdate:zoom"?: ((value: number) => any) | undefined;
|
|
89
88
|
onChange?: ((rect: GrCropRect) => any) | undefined;
|
|
90
89
|
onLoad?: ((size: {
|
|
91
90
|
width: number;
|
|
92
91
|
height: number;
|
|
93
92
|
}) => any) | undefined;
|
|
93
|
+
"onUpdate:zoom"?: ((value: number) => any) | undefined;
|
|
94
94
|
}>, {
|
|
95
95
|
aspectRatio: number;
|
|
96
96
|
output: GrImageCropOutput;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Размер результата по заданной части `output`.
|
|
3
|
+
*
|
|
4
|
+
* Ловушка, ради которой это отдельная функция: задать **одну** сторону —
|
|
5
|
+
* обычное дело («аватар шириной 256»), и вторая обязана считаться из
|
|
6
|
+
* соотношения захваченной области. Взятая из исходника, она даёт растянутую
|
|
7
|
+
* картинку: кадр камеры 640×480 при `output.width = 800` превращался в холст
|
|
8
|
+
* 800×480, то есть в изображение, растянутое по горизонтали на четверть.
|
|
9
|
+
*
|
|
10
|
+
* Обе стороны заданы — это **габарит**, а не точный размер: кадр вписывается в
|
|
11
|
+
* него, сохраняя свои пропорции. Взять их буквально значило бы растянуть
|
|
12
|
+
* картинку, когда соотношения не совпали, — а пропорции съёмки в этом пакете
|
|
13
|
+
* не искажаются никогда.
|
|
14
|
+
*/
|
|
15
|
+
export interface GrOutputRequest {
|
|
16
|
+
width?: number;
|
|
17
|
+
height?: number;
|
|
18
|
+
}
|
|
19
|
+
export interface GrOutputSize {
|
|
20
|
+
width: number;
|
|
21
|
+
height: number;
|
|
22
|
+
}
|
|
23
|
+
export declare function outputSize(area: {
|
|
24
|
+
sw: number;
|
|
25
|
+
sh: number;
|
|
26
|
+
}, output?: GrOutputRequest): GrOutputSize;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@feugene/granularity-media",
|
|
3
3
|
"description": "Media components for @feugene/granularity: image cropping, camera capture, code scanning and video playback.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Evgeniy Fureev",
|
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
import { a as e, c as t, l as n, n as r, o as i, r as a, s as o, u as s } from "../../../chunks/config-CZ4ZDG0_.js";
|
|
2
|
-
import { computed as c, createBlock as l, createCommentVNode as u, createElementBlock as d, createElementVNode as f, createTextVNode as p, createVNode as m, defineComponent as h, normalizeClass as g, normalizeStyle as _, onBeforeUnmount as v, onMounted as y, openBlock as b, ref as x, renderSlot as S, shallowRef as C, toDisplayString as w, unref as T, withCtx as E } from "vue";
|
|
3
|
-
import D from "@feugene/granularity/components/GrButton";
|
|
4
|
-
import { useAnnouncer as O } from "@feugene/granularity/composables/useAnnouncer";
|
|
5
|
-
import { useGrComponentSize as k } from "@feugene/granularity/composables/useGrComponentConfig";
|
|
6
|
-
import { useGranularityTranslations as A } from "@feugene/granularity/composables/useGranularityTranslations";
|
|
7
|
-
//#region src/components/GrCameraCapture/cameraState.ts
|
|
8
|
-
function j(e) {
|
|
9
|
-
switch (e instanceof Error ? e.name : "") {
|
|
10
|
-
case "NotAllowedError":
|
|
11
|
-
case "SecurityError":
|
|
12
|
-
case "PermissionDeniedError": return "denied";
|
|
13
|
-
case "NotFoundError":
|
|
14
|
-
case "DevicesNotFoundError":
|
|
15
|
-
case "OverconstrainedError": return "missing";
|
|
16
|
-
case "NotReadableError":
|
|
17
|
-
case "TrackStartError":
|
|
18
|
-
case "AbortError": return "busy";
|
|
19
|
-
default: return "error";
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
function M(e) {
|
|
23
|
-
return typeof e?.mediaDevices?.getUserMedia == "function";
|
|
24
|
-
}
|
|
25
|
-
function N(e, t) {
|
|
26
|
-
return t ?? e === "user";
|
|
27
|
-
}
|
|
28
|
-
function P(e, t) {
|
|
29
|
-
let n = t > 0 ? t : 1;
|
|
30
|
-
if (e.width <= 0 || e.height <= 0) return {
|
|
31
|
-
sx: 0,
|
|
32
|
-
sy: 0,
|
|
33
|
-
sw: 0,
|
|
34
|
-
sh: 0
|
|
35
|
-
};
|
|
36
|
-
if (e.width / e.height > n) {
|
|
37
|
-
let t = e.height * n;
|
|
38
|
-
return {
|
|
39
|
-
sx: (e.width - t) / 2,
|
|
40
|
-
sy: 0,
|
|
41
|
-
sw: t,
|
|
42
|
-
sh: e.height
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
let r = e.width / n;
|
|
46
|
-
return {
|
|
47
|
-
sx: 0,
|
|
48
|
-
sy: (e.height - r) / 2,
|
|
49
|
-
sw: e.width,
|
|
50
|
-
sh: r
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
//#endregion
|
|
54
|
-
//#region src/components/GrCameraCapture/GrCameraCapture.vue?vue&type=script&setup=true&lang.ts
|
|
55
|
-
var F = ["aria-label"], I = /* @__PURE__ */ h({
|
|
56
|
-
__name: "GrCameraCapture",
|
|
57
|
-
props: {
|
|
58
|
-
facing: { default: "user" },
|
|
59
|
-
deviceId: { default: void 0 },
|
|
60
|
-
autoStart: {
|
|
61
|
-
type: Boolean,
|
|
62
|
-
default: !1
|
|
63
|
-
},
|
|
64
|
-
aspectRatio: { default: 4 / 3 },
|
|
65
|
-
mirror: {
|
|
66
|
-
type: Boolean,
|
|
67
|
-
default: void 0
|
|
68
|
-
},
|
|
69
|
-
output: { default: void 0 },
|
|
70
|
-
size: { default: void 0 },
|
|
71
|
-
disabled: {
|
|
72
|
-
type: Boolean,
|
|
73
|
-
default: !1
|
|
74
|
-
},
|
|
75
|
-
ariaLabel: { default: void 0 }
|
|
76
|
-
},
|
|
77
|
-
emits: [
|
|
78
|
-
"capture",
|
|
79
|
-
"start",
|
|
80
|
-
"stop",
|
|
81
|
-
"statusChange"
|
|
82
|
-
],
|
|
83
|
-
setup(h, { expose: I, emit: L }) {
|
|
84
|
-
let R = h, z = L, { t: B } = A(), { announce: V } = O(), H = k(() => R.size, { component: "GrCameraCapture" }), U = x(null), W = C(null), G = x("idle"), K = c(() => N(R.facing, R.mirror)), q = c(() => G.value === "live"), J = c(() => {
|
|
85
|
-
switch (G.value) {
|
|
86
|
-
case "starting": return B("grMedia.camera.starting", "Requesting camera access…");
|
|
87
|
-
case "denied": return B("grMedia.camera.denied", "Camera access is blocked. Allow it in the browser settings for this site.");
|
|
88
|
-
case "missing": return B("grMedia.camera.missing", "No camera found on this device.");
|
|
89
|
-
case "busy": return B("grMedia.camera.busy", "The camera is busy in another application.");
|
|
90
|
-
case "insecure": return B("grMedia.camera.insecure", "The camera works only over HTTPS.");
|
|
91
|
-
case "error": return B("grMedia.camera.error", "The camera could not be started.");
|
|
92
|
-
default: return B("grMedia.camera.idle", "The camera is off.");
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
function Y(e) {
|
|
96
|
-
G.value !== e && (G.value = e, z("statusChange", e));
|
|
97
|
-
}
|
|
98
|
-
function X() {
|
|
99
|
-
W.value?.getTracks().forEach((e) => e.stop()), W.value = null, U.value && (U.value.srcObject = null);
|
|
100
|
-
}
|
|
101
|
-
async function Z() {
|
|
102
|
-
if (!(R.disabled || G.value === "starting" || G.value === "live")) {
|
|
103
|
-
if (!M(navigator)) {
|
|
104
|
-
Y("insecure");
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
Y("starting");
|
|
108
|
-
try {
|
|
109
|
-
let e = await navigator.mediaDevices.getUserMedia({
|
|
110
|
-
video: R.deviceId ? { deviceId: { exact: R.deviceId } } : { facingMode: R.facing },
|
|
111
|
-
audio: !1
|
|
112
|
-
});
|
|
113
|
-
W.value = e, U.value && (U.value.srcObject = e, await U.value.play().catch(() => void 0)), Y("live"), z("start"), V(B("grMedia.camera.live", "Camera is on"));
|
|
114
|
-
} catch (e) {
|
|
115
|
-
X(), Y(j(e)), V(J.value, { politeness: "assertive" });
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
function Q() {
|
|
120
|
-
W.value && (X(), Y("idle"), z("stop"));
|
|
121
|
-
}
|
|
122
|
-
async function $() {
|
|
123
|
-
let e = U.value;
|
|
124
|
-
if (!e || !q.value) return null;
|
|
125
|
-
let t = P({
|
|
126
|
-
width: e.videoWidth,
|
|
127
|
-
height: e.videoHeight
|
|
128
|
-
}, R.aspectRatio);
|
|
129
|
-
if (t.sw <= 0) return null;
|
|
130
|
-
let n = document.createElement("canvas");
|
|
131
|
-
n.width = Math.max(1, Math.round(R.output?.width ?? t.sw)), n.height = Math.max(1, Math.round(R.output?.height ?? t.sh));
|
|
132
|
-
let r = n.getContext("2d");
|
|
133
|
-
if (!r) return null;
|
|
134
|
-
r.drawImage(e, t.sx, t.sy, t.sw, t.sh, 0, 0, n.width, n.height);
|
|
135
|
-
let i = await new Promise((e) => {
|
|
136
|
-
n.toBlob(e, R.output?.type ?? "image/jpeg", R.output?.quality ?? .92);
|
|
137
|
-
});
|
|
138
|
-
return i && (z("capture", i), V(B("grMedia.camera.captured", "Photo taken"))), i;
|
|
139
|
-
}
|
|
140
|
-
return y(() => {
|
|
141
|
-
R.autoStart && Z();
|
|
142
|
-
}), v(X), I({
|
|
143
|
-
start: Z,
|
|
144
|
-
stop: Q,
|
|
145
|
-
capture: $,
|
|
146
|
-
status: G
|
|
147
|
-
}), (c, v) => (b(), d("div", {
|
|
148
|
-
class: g(T(i)),
|
|
149
|
-
role: "group",
|
|
150
|
-
"aria-label": h.ariaLabel ?? T(B)("grMedia.camera.label", "Camera")
|
|
151
|
-
}, [f("div", {
|
|
152
|
-
class: g(T(a)),
|
|
153
|
-
style: _({ aspectRatio: String(h.aspectRatio) })
|
|
154
|
-
}, [f("video", {
|
|
155
|
-
ref_key: "videoEl",
|
|
156
|
-
ref: U,
|
|
157
|
-
class: g([T(s), K.value ? T(e) : ""]),
|
|
158
|
-
playsinline: "",
|
|
159
|
-
muted: "",
|
|
160
|
-
autoplay: ""
|
|
161
|
-
}, null, 2), q.value ? u("", !0) : (b(), d("div", {
|
|
162
|
-
key: 0,
|
|
163
|
-
class: g(T(t))
|
|
164
|
-
}, [f("p", { class: g([T(n), T(o)[T(H)]]) }, w(J.value), 3), G.value !== "starting" && G.value !== "insecure" && G.value !== "missing" ? (b(), l(T(D), {
|
|
165
|
-
key: 0,
|
|
166
|
-
size: T(H),
|
|
167
|
-
disabled: h.disabled,
|
|
168
|
-
onClick: Z
|
|
169
|
-
}, {
|
|
170
|
-
default: E(() => [p(w(G.value === "idle" ? T(B)("grMedia.camera.start", "Turn on camera") : T(B)("grMedia.camera.retry", "Try again")), 1)]),
|
|
171
|
-
_: 1
|
|
172
|
-
}, 8, ["size", "disabled"])) : u("", !0)], 2))], 6), f("div", { class: g(T(r)) }, [S(c.$slots, "controls", {
|
|
173
|
-
status: G.value,
|
|
174
|
-
start: Z,
|
|
175
|
-
stop: Q,
|
|
176
|
-
capture: $
|
|
177
|
-
}, () => [m(T(D), {
|
|
178
|
-
size: T(H),
|
|
179
|
-
disabled: h.disabled || !q.value,
|
|
180
|
-
onClick: $
|
|
181
|
-
}, {
|
|
182
|
-
default: E(() => [p(w(T(B)("grMedia.camera.capture", "Take photo")), 1)]),
|
|
183
|
-
_: 1
|
|
184
|
-
}, 8, ["size", "disabled"]), q.value ? (b(), l(T(D), {
|
|
185
|
-
key: 0,
|
|
186
|
-
variant: "outline",
|
|
187
|
-
size: T(H),
|
|
188
|
-
disabled: h.disabled,
|
|
189
|
-
onClick: Q
|
|
190
|
-
}, {
|
|
191
|
-
default: E(() => [p(w(T(B)("grMedia.camera.stop", "Turn off")), 1)]),
|
|
192
|
-
_: 1
|
|
193
|
-
}, 8, ["size", "disabled"])) : u("", !0)])], 2)], 10, F));
|
|
194
|
-
}
|
|
195
|
-
});
|
|
196
|
-
//#endregion
|
|
197
|
-
export { N as a, M as i, P as n, j as r, I as t };
|