@capgo/camera-preview 7.4.0-beta.1 → 7.4.0-beta.11
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/README.md +195 -31
- package/android/.gradle/8.14.2/checksums/checksums.lock +0 -0
- package/android/.gradle/8.14.2/checksums/md5-checksums.bin +0 -0
- package/android/.gradle/8.14.2/checksums/sha1-checksums.bin +0 -0
- package/android/.gradle/8.14.2/executionHistory/executionHistory.bin +0 -0
- package/android/.gradle/8.14.2/executionHistory/executionHistory.lock +0 -0
- package/android/.gradle/8.14.2/fileHashes/fileHashes.bin +0 -0
- package/android/.gradle/8.14.2/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/8.14.2/fileHashes/resourceHashesCache.bin +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/file-system.probe +0 -0
- package/android/build.gradle +3 -1
- package/android/src/main/AndroidManifest.xml +5 -3
- package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraPreview.java +473 -88
- package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraXView.java +2065 -704
- package/android/src/main/java/com/ahm/capacitor/camera/preview/GridOverlayView.java +95 -0
- package/android/src/main/java/com/ahm/capacitor/camera/preview/model/CameraDevice.java +55 -46
- package/android/src/main/java/com/ahm/capacitor/camera/preview/model/CameraLens.java +61 -52
- package/android/src/main/java/com/ahm/capacitor/camera/preview/model/CameraSessionConfiguration.java +152 -59
- package/android/src/main/java/com/ahm/capacitor/camera/preview/model/LensInfo.java +29 -23
- package/android/src/main/java/com/ahm/capacitor/camera/preview/model/ZoomFactors.java +24 -23
- package/dist/docs.json +235 -6
- package/dist/esm/definitions.d.ts +119 -3
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +47 -3
- package/dist/esm/web.js +297 -96
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +293 -96
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +293 -96
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CapgoCameraPreview/CameraController.swift +364 -218
- package/ios/Sources/CapgoCameraPreview/GridOverlayView.swift +65 -0
- package/ios/Sources/CapgoCameraPreview/Plugin.swift +886 -242
- package/package.json +1 -1
package/dist/plugin.cjs.js
CHANGED
|
@@ -17,6 +17,7 @@ const CameraPreview = core.registerPlugin("CameraPreview", {
|
|
|
17
17
|
web: () => Promise.resolve().then(function () { return web; }).then((m) => new m.CameraPreviewWeb()),
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
+
const DEFAULT_VIDEO_ID = "capgo_video";
|
|
20
21
|
class CameraPreviewWeb extends core.WebPlugin {
|
|
21
22
|
constructor() {
|
|
22
23
|
super();
|
|
@@ -26,87 +27,104 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
26
27
|
*/
|
|
27
28
|
this.isBackCamera = false;
|
|
28
29
|
this.currentDeviceId = null;
|
|
30
|
+
this.videoElement = null;
|
|
31
|
+
this.isStarted = false;
|
|
29
32
|
}
|
|
30
33
|
async getSupportedPictureSizes() {
|
|
31
34
|
throw new Error("getSupportedPictureSizes not supported under the web platform");
|
|
32
35
|
}
|
|
33
36
|
async start(options) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
stream.getTracks().forEach((track) => track.stop());
|
|
43
|
-
})
|
|
44
|
-
.catch((error) => {
|
|
45
|
-
Promise.reject(error);
|
|
46
|
-
});
|
|
47
|
-
const video = document.getElementById("video");
|
|
37
|
+
if (options.aspectRatio && (options.width || options.height)) {
|
|
38
|
+
throw new Error("Cannot set both aspectRatio and size (width/height). Use setPreviewSize after start.");
|
|
39
|
+
}
|
|
40
|
+
if (this.isStarted) {
|
|
41
|
+
throw new Error("camera already started");
|
|
42
|
+
}
|
|
43
|
+
this.isBackCamera = true;
|
|
44
|
+
this.isStarted = false;
|
|
48
45
|
const parent = document.getElementById((options === null || options === void 0 ? void 0 : options.parent) || "");
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
46
|
+
const gridMode = (options === null || options === void 0 ? void 0 : options.gridMode) || "none";
|
|
47
|
+
if (options.position) {
|
|
48
|
+
this.isBackCamera = options.position === "rear";
|
|
49
|
+
}
|
|
50
|
+
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
51
|
+
if (video) {
|
|
52
|
+
video.remove();
|
|
53
|
+
}
|
|
54
|
+
const container = options.parent
|
|
55
|
+
? document.getElementById(options.parent)
|
|
56
|
+
: document.body;
|
|
57
|
+
if (!container) {
|
|
58
|
+
throw new Error("container not found");
|
|
59
|
+
}
|
|
60
|
+
this.videoElement = document.createElement("video");
|
|
61
|
+
this.videoElement.id = DEFAULT_VIDEO_ID;
|
|
62
|
+
this.videoElement.className = options.className || "";
|
|
63
|
+
this.videoElement.playsInline = true;
|
|
64
|
+
this.videoElement.muted = true;
|
|
65
|
+
this.videoElement.autoplay = true;
|
|
66
|
+
container.appendChild(this.videoElement);
|
|
67
|
+
if (options.toBack) {
|
|
68
|
+
this.videoElement.style.zIndex = "-1";
|
|
69
|
+
}
|
|
70
|
+
if (options.width) {
|
|
71
|
+
this.videoElement.width = options.width;
|
|
72
|
+
}
|
|
73
|
+
if (options.height) {
|
|
74
|
+
this.videoElement.height = options.height;
|
|
75
|
+
}
|
|
76
|
+
if (options.x) {
|
|
77
|
+
this.videoElement.style.left = `${options.x}px`;
|
|
78
|
+
}
|
|
79
|
+
// Create and add grid overlay if needed
|
|
80
|
+
if (gridMode !== "none") {
|
|
81
|
+
const gridOverlay = this.createGridOverlay(gridMode);
|
|
82
|
+
gridOverlay.id = "camera-grid-overlay";
|
|
83
|
+
parent === null || parent === void 0 ? void 0 : parent.appendChild(gridOverlay);
|
|
84
|
+
}
|
|
85
|
+
if (options.y) {
|
|
86
|
+
this.videoElement.style.top = `${options.y}px`;
|
|
87
|
+
}
|
|
88
|
+
if (options.aspectRatio) {
|
|
89
|
+
const [widthRatio, heightRatio] = options.aspectRatio
|
|
90
|
+
.split(":")
|
|
91
|
+
.map(Number);
|
|
92
|
+
const ratio = widthRatio / heightRatio;
|
|
93
|
+
if (options.width) {
|
|
94
|
+
this.videoElement.height = options.width / ratio;
|
|
56
95
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
// Safari on iOS needs to have the autoplay, muted and playsinline attributes set for video.play() to be successful
|
|
60
|
-
// Without these attributes videoElement.play() will throw a NotAllowedError
|
|
61
|
-
// https://developer.apple.com/documentation/webkit/delivering_video_content_for_safari
|
|
62
|
-
if (isSafari) {
|
|
63
|
-
videoElement.setAttribute("autoplay", "true");
|
|
64
|
-
videoElement.setAttribute("muted", "true");
|
|
65
|
-
videoElement.setAttribute("playsinline", "true");
|
|
66
|
-
}
|
|
67
|
-
parent === null || parent === void 0 ? void 0 : parent.appendChild(videoElement);
|
|
68
|
-
if ((_a = navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.getUserMedia) {
|
|
69
|
-
const constraints = {
|
|
70
|
-
video: {
|
|
71
|
-
width: { ideal: options.width },
|
|
72
|
-
height: { ideal: options.height },
|
|
73
|
-
},
|
|
74
|
-
};
|
|
75
|
-
if (options.deviceId) {
|
|
76
|
-
constraints.video.deviceId = { exact: options.deviceId };
|
|
77
|
-
this.currentDeviceId = options.deviceId;
|
|
78
|
-
// Try to determine camera position from device
|
|
79
|
-
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
80
|
-
const device = devices.find(d => d.deviceId === options.deviceId);
|
|
81
|
-
this.isBackCamera = (device === null || device === void 0 ? void 0 : device.label.toLowerCase().includes('back')) || (device === null || device === void 0 ? void 0 : device.label.toLowerCase().includes('rear')) || false;
|
|
82
|
-
}
|
|
83
|
-
else if (options.position === "rear") {
|
|
84
|
-
constraints.video.facingMode = "environment";
|
|
85
|
-
this.isBackCamera = true;
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
this.isBackCamera = false;
|
|
89
|
-
}
|
|
90
|
-
const self = this;
|
|
91
|
-
await navigator.mediaDevices.getUserMedia(constraints).then((stream) => {
|
|
92
|
-
if (document.getElementById("video")) {
|
|
93
|
-
// video.src = window.URL.createObjectURL(stream);
|
|
94
|
-
videoElement.srcObject = stream;
|
|
95
|
-
videoElement.play();
|
|
96
|
-
Promise.resolve({});
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
self.stopStream(stream);
|
|
100
|
-
Promise.reject(new Error("camera already stopped"));
|
|
101
|
-
}
|
|
102
|
-
}, (err) => {
|
|
103
|
-
Promise.reject(new Error(err));
|
|
104
|
-
});
|
|
96
|
+
else if (options.height) {
|
|
97
|
+
this.videoElement.width = options.height * ratio;
|
|
105
98
|
}
|
|
106
99
|
}
|
|
107
100
|
else {
|
|
108
|
-
|
|
101
|
+
this.videoElement.style.objectFit = "cover";
|
|
102
|
+
}
|
|
103
|
+
const constraints = {
|
|
104
|
+
video: {
|
|
105
|
+
width: { ideal: this.videoElement.width || 640 },
|
|
106
|
+
height: { ideal: this.videoElement.height || window.innerHeight },
|
|
107
|
+
facingMode: this.isBackCamera ? "environment" : "user",
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
const stream = await navigator.mediaDevices.getUserMedia(constraints);
|
|
111
|
+
if (!stream) {
|
|
112
|
+
throw new Error("could not acquire stream");
|
|
109
113
|
}
|
|
114
|
+
if (!this.videoElement) {
|
|
115
|
+
throw new Error("video element not found");
|
|
116
|
+
}
|
|
117
|
+
this.videoElement.srcObject = stream;
|
|
118
|
+
if (!this.isBackCamera) {
|
|
119
|
+
this.videoElement.style.transform = "scaleX(-1)";
|
|
120
|
+
}
|
|
121
|
+
this.isStarted = true;
|
|
122
|
+
return {
|
|
123
|
+
width: this.videoElement.width,
|
|
124
|
+
height: this.videoElement.height,
|
|
125
|
+
x: this.videoElement.getBoundingClientRect().x,
|
|
126
|
+
y: this.videoElement.getBoundingClientRect().y,
|
|
127
|
+
};
|
|
110
128
|
}
|
|
111
129
|
stopStream(stream) {
|
|
112
130
|
if (stream) {
|
|
@@ -116,16 +134,20 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
116
134
|
}
|
|
117
135
|
}
|
|
118
136
|
async stop() {
|
|
119
|
-
const video = document.getElementById(
|
|
137
|
+
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
120
138
|
if (video) {
|
|
121
139
|
video.pause();
|
|
122
140
|
this.stopStream(video.srcObject);
|
|
123
141
|
video.remove();
|
|
142
|
+
this.isStarted = false;
|
|
124
143
|
}
|
|
144
|
+
// Remove grid overlay if it exists
|
|
145
|
+
const gridOverlay = document.getElementById("camera-grid-overlay");
|
|
146
|
+
gridOverlay === null || gridOverlay === void 0 ? void 0 : gridOverlay.remove();
|
|
125
147
|
}
|
|
126
148
|
async capture(options) {
|
|
127
149
|
return new Promise((resolve, reject) => {
|
|
128
|
-
const video = document.getElementById(
|
|
150
|
+
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
129
151
|
if (!(video === null || video === void 0 ? void 0 : video.srcObject)) {
|
|
130
152
|
reject(new Error("camera is not running"));
|
|
131
153
|
return;
|
|
@@ -143,6 +165,8 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
143
165
|
context === null || context === void 0 ? void 0 : context.scale(-1, 1);
|
|
144
166
|
}
|
|
145
167
|
context === null || context === void 0 ? void 0 : context.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
|
|
168
|
+
if (options.saveToGallery) ;
|
|
169
|
+
if (options.withExifLocation) ;
|
|
146
170
|
if ((options.format || "jpeg") === "jpeg") {
|
|
147
171
|
base64EncodedImage = canvas
|
|
148
172
|
.toDataURL("image/jpeg", (options.quality || 85) / 100.0)
|
|
@@ -156,6 +180,7 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
156
180
|
}
|
|
157
181
|
resolve({
|
|
158
182
|
value: base64EncodedImage,
|
|
183
|
+
exif: {},
|
|
159
184
|
});
|
|
160
185
|
});
|
|
161
186
|
}
|
|
@@ -179,7 +204,7 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
179
204
|
throw new Error(`setFlashMode not supported under the web platform${_options}`);
|
|
180
205
|
}
|
|
181
206
|
async flip() {
|
|
182
|
-
const video = document.getElementById(
|
|
207
|
+
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
183
208
|
if (!(video === null || video === void 0 ? void 0 : video.srcObject)) {
|
|
184
209
|
throw new Error("camera is not running");
|
|
185
210
|
}
|
|
@@ -219,12 +244,12 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
219
244
|
}
|
|
220
245
|
}
|
|
221
246
|
async setOpacity(_options) {
|
|
222
|
-
const video = document.getElementById(
|
|
247
|
+
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
223
248
|
if (!!video && !!_options.opacity)
|
|
224
249
|
video.style.setProperty("opacity", _options.opacity.toString());
|
|
225
250
|
}
|
|
226
251
|
async isRunning() {
|
|
227
|
-
const video = document.getElementById(
|
|
252
|
+
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
228
253
|
return { isRunning: !!video && !!video.srcObject };
|
|
229
254
|
}
|
|
230
255
|
async getAvailableDevices() {
|
|
@@ -233,7 +258,7 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
233
258
|
throw new Error("getAvailableDevices not supported under the web platform");
|
|
234
259
|
}
|
|
235
260
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
236
|
-
const videoDevices = devices.filter(device => device.kind ===
|
|
261
|
+
const videoDevices = devices.filter((device) => device.kind === "videoinput");
|
|
237
262
|
// Group devices by position (front/back)
|
|
238
263
|
const frontDevices = [];
|
|
239
264
|
const backDevices = [];
|
|
@@ -243,15 +268,19 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
243
268
|
// Determine device type based on label
|
|
244
269
|
let deviceType = exports.DeviceType.WIDE_ANGLE;
|
|
245
270
|
let baseZoomRatio = 1.0;
|
|
246
|
-
if (labelLower.includes(
|
|
271
|
+
if (labelLower.includes("ultra") || labelLower.includes("0.5")) {
|
|
247
272
|
deviceType = exports.DeviceType.ULTRA_WIDE;
|
|
248
273
|
baseZoomRatio = 0.5;
|
|
249
274
|
}
|
|
250
|
-
else if (labelLower.includes(
|
|
275
|
+
else if (labelLower.includes("telephoto") ||
|
|
276
|
+
labelLower.includes("tele") ||
|
|
277
|
+
labelLower.includes("2x") ||
|
|
278
|
+
labelLower.includes("3x")) {
|
|
251
279
|
deviceType = exports.DeviceType.TELEPHOTO;
|
|
252
280
|
baseZoomRatio = 2.0;
|
|
253
281
|
}
|
|
254
|
-
else if (labelLower.includes(
|
|
282
|
+
else if (labelLower.includes("depth") ||
|
|
283
|
+
labelLower.includes("truedepth")) {
|
|
255
284
|
deviceType = exports.DeviceType.TRUE_DEPTH;
|
|
256
285
|
baseZoomRatio = 1.0;
|
|
257
286
|
}
|
|
@@ -262,10 +291,10 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
262
291
|
focalLength: 4.25,
|
|
263
292
|
baseZoomRatio,
|
|
264
293
|
minZoom: 1.0,
|
|
265
|
-
maxZoom: 1.0
|
|
294
|
+
maxZoom: 1.0,
|
|
266
295
|
};
|
|
267
296
|
// Determine position and add to appropriate array
|
|
268
|
-
if (labelLower.includes(
|
|
297
|
+
if (labelLower.includes("back") || labelLower.includes("rear")) {
|
|
269
298
|
backDevices.push(lensInfo);
|
|
270
299
|
}
|
|
271
300
|
else {
|
|
@@ -280,8 +309,8 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
280
309
|
position: "front",
|
|
281
310
|
lenses: frontDevices,
|
|
282
311
|
isLogical: false,
|
|
283
|
-
minZoom: Math.min(...frontDevices.map(d => d.minZoom)),
|
|
284
|
-
maxZoom: Math.max(...frontDevices.map(d => d.maxZoom))
|
|
312
|
+
minZoom: Math.min(...frontDevices.map((d) => d.minZoom)),
|
|
313
|
+
maxZoom: Math.max(...frontDevices.map((d) => d.maxZoom)),
|
|
285
314
|
});
|
|
286
315
|
}
|
|
287
316
|
if (backDevices.length > 0) {
|
|
@@ -291,14 +320,14 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
291
320
|
position: "rear",
|
|
292
321
|
lenses: backDevices,
|
|
293
322
|
isLogical: false,
|
|
294
|
-
minZoom: Math.min(...backDevices.map(d => d.minZoom)),
|
|
295
|
-
maxZoom: Math.max(...backDevices.map(d => d.maxZoom))
|
|
323
|
+
minZoom: Math.min(...backDevices.map((d) => d.minZoom)),
|
|
324
|
+
maxZoom: Math.max(...backDevices.map((d) => d.maxZoom)),
|
|
296
325
|
});
|
|
297
326
|
}
|
|
298
327
|
return { devices: result };
|
|
299
328
|
}
|
|
300
329
|
async getZoom() {
|
|
301
|
-
const video = document.getElementById(
|
|
330
|
+
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
302
331
|
if (!(video === null || video === void 0 ? void 0 : video.srcObject)) {
|
|
303
332
|
throw new Error("camera is not running");
|
|
304
333
|
}
|
|
@@ -317,18 +346,22 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
317
346
|
let baseZoomRatio = 1.0;
|
|
318
347
|
if (this.currentDeviceId) {
|
|
319
348
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
320
|
-
const device = devices.find(d => d.deviceId === this.currentDeviceId);
|
|
349
|
+
const device = devices.find((d) => d.deviceId === this.currentDeviceId);
|
|
321
350
|
if (device) {
|
|
322
351
|
const labelLower = device.label.toLowerCase();
|
|
323
|
-
if (labelLower.includes(
|
|
352
|
+
if (labelLower.includes("ultra") || labelLower.includes("0.5")) {
|
|
324
353
|
deviceType = exports.DeviceType.ULTRA_WIDE;
|
|
325
354
|
baseZoomRatio = 0.5;
|
|
326
355
|
}
|
|
327
|
-
else if (labelLower.includes(
|
|
356
|
+
else if (labelLower.includes("telephoto") ||
|
|
357
|
+
labelLower.includes("tele") ||
|
|
358
|
+
labelLower.includes("2x") ||
|
|
359
|
+
labelLower.includes("3x")) {
|
|
328
360
|
deviceType = exports.DeviceType.TELEPHOTO;
|
|
329
361
|
baseZoomRatio = 2.0;
|
|
330
362
|
}
|
|
331
|
-
else if (labelLower.includes(
|
|
363
|
+
else if (labelLower.includes("depth") ||
|
|
364
|
+
labelLower.includes("truedepth")) {
|
|
332
365
|
deviceType = exports.DeviceType.TRUE_DEPTH;
|
|
333
366
|
baseZoomRatio = 1.0;
|
|
334
367
|
}
|
|
@@ -339,7 +372,7 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
339
372
|
focalLength: 4.25,
|
|
340
373
|
deviceType,
|
|
341
374
|
baseZoomRatio,
|
|
342
|
-
digitalZoom: currentZoom / baseZoomRatio
|
|
375
|
+
digitalZoom: currentZoom / baseZoomRatio,
|
|
343
376
|
};
|
|
344
377
|
return {
|
|
345
378
|
min: capabilities.zoom.min || 1,
|
|
@@ -349,7 +382,7 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
349
382
|
};
|
|
350
383
|
}
|
|
351
384
|
async setZoom(options) {
|
|
352
|
-
const video = document.getElementById(
|
|
385
|
+
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
353
386
|
if (!(video === null || video === void 0 ? void 0 : video.srcObject)) {
|
|
354
387
|
throw new Error("camera is not running");
|
|
355
388
|
}
|
|
@@ -365,7 +398,7 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
365
398
|
const zoomLevel = Math.max(capabilities.zoom.min || 1, Math.min(capabilities.zoom.max || 1, options.level));
|
|
366
399
|
try {
|
|
367
400
|
await videoTrack.applyConstraints({
|
|
368
|
-
advanced: [{ zoom: zoomLevel }]
|
|
401
|
+
advanced: [{ zoom: zoomLevel }],
|
|
369
402
|
});
|
|
370
403
|
}
|
|
371
404
|
catch (error) {
|
|
@@ -379,7 +412,7 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
379
412
|
return { deviceId: this.currentDeviceId || "" };
|
|
380
413
|
}
|
|
381
414
|
async setDeviceId(options) {
|
|
382
|
-
const video = document.getElementById(
|
|
415
|
+
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
383
416
|
if (!(video === null || video === void 0 ? void 0 : video.srcObject)) {
|
|
384
417
|
throw new Error("camera is not running");
|
|
385
418
|
}
|
|
@@ -398,8 +431,11 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
398
431
|
try {
|
|
399
432
|
// Try to determine camera position from device
|
|
400
433
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
401
|
-
const device = devices.find(d => d.deviceId === options.deviceId);
|
|
402
|
-
this.isBackCamera =
|
|
434
|
+
const device = devices.find((d) => d.deviceId === options.deviceId);
|
|
435
|
+
this.isBackCamera =
|
|
436
|
+
(device === null || device === void 0 ? void 0 : device.label.toLowerCase().includes("back")) ||
|
|
437
|
+
(device === null || device === void 0 ? void 0 : device.label.toLowerCase().includes("rear")) ||
|
|
438
|
+
false;
|
|
403
439
|
const stream = await navigator.mediaDevices.getUserMedia(constraints);
|
|
404
440
|
video.srcObject = stream;
|
|
405
441
|
// Update video transform based on camera
|
|
@@ -417,6 +453,167 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
417
453
|
throw new Error(`Failed to swap to device ${options.deviceId}: ${error}`);
|
|
418
454
|
}
|
|
419
455
|
}
|
|
456
|
+
async getAspectRatio() {
|
|
457
|
+
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
458
|
+
if (!video) {
|
|
459
|
+
throw new Error("camera is not running");
|
|
460
|
+
}
|
|
461
|
+
const width = video.offsetWidth;
|
|
462
|
+
const height = video.offsetHeight;
|
|
463
|
+
if (width && height) {
|
|
464
|
+
const ratio = width / height;
|
|
465
|
+
// Check for portrait camera ratios: 4:3 -> 3:4, 16:9 -> 9:16
|
|
466
|
+
if (Math.abs(ratio - 3 / 4) < 0.01) {
|
|
467
|
+
return { aspectRatio: "4:3" };
|
|
468
|
+
}
|
|
469
|
+
if (Math.abs(ratio - 9 / 16) < 0.01) {
|
|
470
|
+
return { aspectRatio: "16:9" };
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
// Default to 4:3 if no specific aspect ratio is matched
|
|
474
|
+
return { aspectRatio: "4:3" };
|
|
475
|
+
}
|
|
476
|
+
async setAspectRatio(options) {
|
|
477
|
+
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
478
|
+
if (!video) {
|
|
479
|
+
throw new Error("camera is not running");
|
|
480
|
+
}
|
|
481
|
+
if (options.aspectRatio) {
|
|
482
|
+
const [widthRatio, heightRatio] = options.aspectRatio
|
|
483
|
+
.split(":")
|
|
484
|
+
.map(Number);
|
|
485
|
+
// For camera, use portrait orientation: 4:3 becomes 3:4, 16:9 becomes 9:16
|
|
486
|
+
const ratio = heightRatio / widthRatio;
|
|
487
|
+
// Get current position and size
|
|
488
|
+
const rect = video.getBoundingClientRect();
|
|
489
|
+
const currentWidth = rect.width;
|
|
490
|
+
const currentHeight = rect.height;
|
|
491
|
+
const currentRatio = currentWidth / currentHeight;
|
|
492
|
+
let newWidth;
|
|
493
|
+
let newHeight;
|
|
494
|
+
if (currentRatio > ratio) {
|
|
495
|
+
// Width is larger, fit by height and center horizontally
|
|
496
|
+
newWidth = currentHeight * ratio;
|
|
497
|
+
newHeight = currentHeight;
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
// Height is larger, fit by width and center vertically
|
|
501
|
+
newWidth = currentWidth;
|
|
502
|
+
newHeight = currentWidth / ratio;
|
|
503
|
+
}
|
|
504
|
+
// Calculate position
|
|
505
|
+
let x, y;
|
|
506
|
+
if (options.x !== undefined && options.y !== undefined) {
|
|
507
|
+
// Use provided coordinates, ensuring they stay within screen boundaries
|
|
508
|
+
x = Math.max(0, Math.min(options.x, window.innerWidth - newWidth));
|
|
509
|
+
y = Math.max(0, Math.min(options.y, window.innerHeight - newHeight));
|
|
510
|
+
}
|
|
511
|
+
else {
|
|
512
|
+
// Auto-center the view
|
|
513
|
+
x = (window.innerWidth - newWidth) / 2;
|
|
514
|
+
y = (window.innerHeight - newHeight) / 2;
|
|
515
|
+
}
|
|
516
|
+
video.style.width = `${newWidth}px`;
|
|
517
|
+
video.style.height = `${newHeight}px`;
|
|
518
|
+
video.style.left = `${x}px`;
|
|
519
|
+
video.style.top = `${y}px`;
|
|
520
|
+
video.style.position = "absolute";
|
|
521
|
+
return {
|
|
522
|
+
width: Math.round(newWidth),
|
|
523
|
+
height: Math.round(newHeight),
|
|
524
|
+
x: Math.round(x),
|
|
525
|
+
y: Math.round(y),
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
529
|
+
video.style.objectFit = "cover";
|
|
530
|
+
const rect = video.getBoundingClientRect();
|
|
531
|
+
return {
|
|
532
|
+
width: Math.round(rect.width),
|
|
533
|
+
height: Math.round(rect.height),
|
|
534
|
+
x: Math.round(rect.left),
|
|
535
|
+
y: Math.round(rect.top),
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
createGridOverlay(gridMode) {
|
|
540
|
+
const overlay = document.createElement("div");
|
|
541
|
+
overlay.style.position = "absolute";
|
|
542
|
+
overlay.style.top = "0";
|
|
543
|
+
overlay.style.left = "0";
|
|
544
|
+
overlay.style.width = "100%";
|
|
545
|
+
overlay.style.height = "100%";
|
|
546
|
+
overlay.style.pointerEvents = "none";
|
|
547
|
+
overlay.style.zIndex = "10";
|
|
548
|
+
const divisions = gridMode === "3x3" ? 3 : 4;
|
|
549
|
+
// Create SVG for grid lines
|
|
550
|
+
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
551
|
+
svg.style.width = "100%";
|
|
552
|
+
svg.style.height = "100%";
|
|
553
|
+
svg.style.position = "absolute";
|
|
554
|
+
svg.style.top = "0";
|
|
555
|
+
svg.style.left = "0";
|
|
556
|
+
// Create grid lines
|
|
557
|
+
for (let i = 1; i < divisions; i++) {
|
|
558
|
+
// Vertical lines
|
|
559
|
+
const verticalLine = document.createElementNS("http://www.w3.org/2000/svg", "line");
|
|
560
|
+
verticalLine.setAttribute("x1", `${(i / divisions) * 100}%`);
|
|
561
|
+
verticalLine.setAttribute("y1", "0%");
|
|
562
|
+
verticalLine.setAttribute("x2", `${(i / divisions) * 100}%`);
|
|
563
|
+
verticalLine.setAttribute("y2", "100%");
|
|
564
|
+
verticalLine.setAttribute("stroke", "rgba(255, 255, 255, 0.5)");
|
|
565
|
+
verticalLine.setAttribute("stroke-width", "1");
|
|
566
|
+
svg.appendChild(verticalLine);
|
|
567
|
+
// Horizontal lines
|
|
568
|
+
const horizontalLine = document.createElementNS("http://www.w3.org/2000/svg", "line");
|
|
569
|
+
horizontalLine.setAttribute("x1", "0%");
|
|
570
|
+
horizontalLine.setAttribute("y1", `${(i / divisions) * 100}%`);
|
|
571
|
+
horizontalLine.setAttribute("x2", "100%");
|
|
572
|
+
horizontalLine.setAttribute("y2", `${(i / divisions) * 100}%`);
|
|
573
|
+
horizontalLine.setAttribute("stroke", "rgba(255, 255, 255, 0.5)");
|
|
574
|
+
horizontalLine.setAttribute("stroke-width", "1");
|
|
575
|
+
svg.appendChild(horizontalLine);
|
|
576
|
+
}
|
|
577
|
+
overlay.appendChild(svg);
|
|
578
|
+
return overlay;
|
|
579
|
+
}
|
|
580
|
+
async setGridMode(options) {
|
|
581
|
+
// Web implementation of grid mode would need to be implemented
|
|
582
|
+
// For now, just resolve as a no-op
|
|
583
|
+
console.warn(`Grid mode '${options.gridMode}' is not yet implemented for web platform`);
|
|
584
|
+
}
|
|
585
|
+
async getGridMode() {
|
|
586
|
+
// Web implementation - default to none
|
|
587
|
+
return { gridMode: "none" };
|
|
588
|
+
}
|
|
589
|
+
async getPreviewSize() {
|
|
590
|
+
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
591
|
+
if (!video) {
|
|
592
|
+
throw new Error("camera is not running");
|
|
593
|
+
}
|
|
594
|
+
return {
|
|
595
|
+
x: video.offsetLeft,
|
|
596
|
+
y: video.offsetTop,
|
|
597
|
+
width: video.width,
|
|
598
|
+
height: video.height,
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
async setPreviewSize(options) {
|
|
602
|
+
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
603
|
+
if (!video) {
|
|
604
|
+
throw new Error("camera is not running");
|
|
605
|
+
}
|
|
606
|
+
video.style.left = `${options.x}px`;
|
|
607
|
+
video.style.top = `${options.y}px`;
|
|
608
|
+
video.width = options.width;
|
|
609
|
+
video.height = options.height;
|
|
610
|
+
return {
|
|
611
|
+
width: options.width,
|
|
612
|
+
height: options.height,
|
|
613
|
+
x: options.x,
|
|
614
|
+
y: options.y,
|
|
615
|
+
};
|
|
616
|
+
}
|
|
420
617
|
}
|
|
421
618
|
|
|
422
619
|
var web = /*#__PURE__*/Object.freeze({
|