@capgo/camera-preview 7.4.0-beta.13 → 7.4.0-beta.16
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 +19 -18
- 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/src/main/java/com/ahm/capacitor/camera/preview/CameraPreview.java +271 -20
- package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraXView.java +816 -134
- package/android/src/main/java/com/ahm/capacitor/camera/preview/model/CameraSessionConfiguration.java +9 -0
- package/dist/docs.json +39 -23
- package/dist/esm/definitions.d.ts +20 -10
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +1 -0
- package/dist/esm/web.js +266 -38
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +266 -38
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +266 -38
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CapgoCameraPreview/CameraController.swift +430 -71
- package/ios/Sources/CapgoCameraPreview/Plugin.swift +139 -114
- package/package.json +1 -1
package/dist/esm/web.js
CHANGED
|
@@ -46,47 +46,57 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
46
46
|
this.videoElement.playsInline = true;
|
|
47
47
|
this.videoElement.muted = true;
|
|
48
48
|
this.videoElement.autoplay = true;
|
|
49
|
+
// Remove objectFit as we'll match camera's native aspect ratio
|
|
50
|
+
this.videoElement.style.backgroundColor = "transparent";
|
|
51
|
+
// Reset any default margins that might interfere
|
|
52
|
+
this.videoElement.style.margin = "0";
|
|
53
|
+
this.videoElement.style.padding = "0";
|
|
49
54
|
container.appendChild(this.videoElement);
|
|
50
55
|
if (options.toBack) {
|
|
51
56
|
this.videoElement.style.zIndex = "-1";
|
|
52
57
|
}
|
|
58
|
+
// Default to 16:9 vertical (9:16 for portrait) if no aspect ratio or size specified
|
|
59
|
+
const useDefaultAspectRatio = !options.aspectRatio && !options.width && !options.height;
|
|
60
|
+
const effectiveAspectRatio = options.aspectRatio || (useDefaultAspectRatio ? "16:9" : null);
|
|
53
61
|
if (options.width) {
|
|
54
62
|
this.videoElement.width = options.width;
|
|
63
|
+
this.videoElement.style.width = `${options.width}px`;
|
|
55
64
|
}
|
|
56
65
|
if (options.height) {
|
|
57
66
|
this.videoElement.height = options.height;
|
|
58
|
-
|
|
59
|
-
|
|
67
|
+
this.videoElement.style.height = `${options.height}px`;
|
|
68
|
+
}
|
|
69
|
+
// Handle positioning - center if x or y not provided
|
|
70
|
+
const centerX = options.x === undefined;
|
|
71
|
+
const centerY = options.y === undefined;
|
|
72
|
+
// Always set position to absolute for proper positioning
|
|
73
|
+
this.videoElement.style.position = "absolute";
|
|
74
|
+
console.log("Initial positioning flags:", {
|
|
75
|
+
centerX,
|
|
76
|
+
centerY,
|
|
77
|
+
x: options.x,
|
|
78
|
+
y: options.y,
|
|
79
|
+
});
|
|
80
|
+
if (options.x !== undefined) {
|
|
60
81
|
this.videoElement.style.left = `${options.x}px`;
|
|
61
82
|
}
|
|
83
|
+
if (options.y !== undefined) {
|
|
84
|
+
this.videoElement.style.top = `${options.y}px`;
|
|
85
|
+
}
|
|
62
86
|
// Create and add grid overlay if needed
|
|
63
87
|
if (gridMode !== "none") {
|
|
64
88
|
const gridOverlay = this.createGridOverlay(gridMode);
|
|
65
89
|
gridOverlay.id = "camera-grid-overlay";
|
|
66
90
|
parent === null || parent === void 0 ? void 0 : parent.appendChild(gridOverlay);
|
|
67
91
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
.map(Number);
|
|
75
|
-
const ratio = widthRatio / heightRatio;
|
|
76
|
-
if (options.width) {
|
|
77
|
-
this.videoElement.height = options.width / ratio;
|
|
78
|
-
}
|
|
79
|
-
else if (options.height) {
|
|
80
|
-
this.videoElement.width = options.height * ratio;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
this.videoElement.style.objectFit = "cover";
|
|
85
|
-
}
|
|
92
|
+
// Aspect ratio handling is now done after getting camera stream
|
|
93
|
+
// Store centering flags for later use
|
|
94
|
+
const needsCenterX = centerX;
|
|
95
|
+
const needsCenterY = centerY;
|
|
96
|
+
console.log("Centering flags stored:", { needsCenterX, needsCenterY });
|
|
97
|
+
// First get the camera stream with basic constraints
|
|
86
98
|
const constraints = {
|
|
87
99
|
video: {
|
|
88
|
-
width: { ideal: this.videoElement.width || 640 },
|
|
89
|
-
height: { ideal: this.videoElement.height || window.innerHeight },
|
|
90
100
|
facingMode: this.isBackCamera ? "environment" : "user",
|
|
91
101
|
},
|
|
92
102
|
};
|
|
@@ -97,16 +107,182 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
97
107
|
if (!this.videoElement) {
|
|
98
108
|
throw new Error("video element not found");
|
|
99
109
|
}
|
|
110
|
+
// Get the actual camera dimensions from the video track
|
|
111
|
+
const videoTrack = stream.getVideoTracks()[0];
|
|
112
|
+
const settings = videoTrack.getSettings();
|
|
113
|
+
const cameraWidth = settings.width || 640;
|
|
114
|
+
const cameraHeight = settings.height || 480;
|
|
115
|
+
const cameraAspectRatio = cameraWidth / cameraHeight;
|
|
116
|
+
console.log("Camera native dimensions:", {
|
|
117
|
+
width: cameraWidth,
|
|
118
|
+
height: cameraHeight,
|
|
119
|
+
aspectRatio: cameraAspectRatio,
|
|
120
|
+
});
|
|
121
|
+
console.log("Container dimensions:", {
|
|
122
|
+
width: container.offsetWidth,
|
|
123
|
+
height: container.offsetHeight,
|
|
124
|
+
id: container.id,
|
|
125
|
+
});
|
|
126
|
+
// Now adjust video element size based on camera's native aspect ratio
|
|
127
|
+
if (!options.width && !options.height && !options.aspectRatio) {
|
|
128
|
+
// No size specified, fit camera view within container bounds
|
|
129
|
+
const containerWidth = container.offsetWidth || window.innerWidth;
|
|
130
|
+
const containerHeight = container.offsetHeight || window.innerHeight;
|
|
131
|
+
// Calculate dimensions that fit within container while maintaining camera aspect ratio
|
|
132
|
+
let targetWidth, targetHeight;
|
|
133
|
+
// Try fitting to container width first
|
|
134
|
+
targetWidth = containerWidth;
|
|
135
|
+
targetHeight = targetWidth / cameraAspectRatio;
|
|
136
|
+
// If height exceeds container, fit to height instead
|
|
137
|
+
if (targetHeight > containerHeight) {
|
|
138
|
+
targetHeight = containerHeight;
|
|
139
|
+
targetWidth = targetHeight * cameraAspectRatio;
|
|
140
|
+
}
|
|
141
|
+
console.log("Video element dimensions:", {
|
|
142
|
+
width: targetWidth,
|
|
143
|
+
height: targetHeight,
|
|
144
|
+
container: { width: containerWidth, height: containerHeight },
|
|
145
|
+
});
|
|
146
|
+
this.videoElement.width = targetWidth;
|
|
147
|
+
this.videoElement.height = targetHeight;
|
|
148
|
+
this.videoElement.style.width = `${targetWidth}px`;
|
|
149
|
+
this.videoElement.style.height = `${targetHeight}px`;
|
|
150
|
+
// Center the video element within its parent container
|
|
151
|
+
if (needsCenterX || options.x === undefined) {
|
|
152
|
+
const x = Math.round((containerWidth - targetWidth) / 2);
|
|
153
|
+
this.videoElement.style.left = `${x}px`;
|
|
154
|
+
}
|
|
155
|
+
if (needsCenterY || options.y === undefined) {
|
|
156
|
+
const y = Math.round((window.innerHeight - targetHeight) / 2);
|
|
157
|
+
this.videoElement.style.setProperty("top", `${y}px`, "important");
|
|
158
|
+
// Force a style recalculation
|
|
159
|
+
this.videoElement.offsetHeight;
|
|
160
|
+
console.log("Centering video:", {
|
|
161
|
+
viewportHeight: window.innerHeight,
|
|
162
|
+
targetHeight,
|
|
163
|
+
calculatedY: y,
|
|
164
|
+
actualTop: this.videoElement.style.top,
|
|
165
|
+
position: this.videoElement.style.position,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
else if (effectiveAspectRatio && !options.width && !options.height) {
|
|
170
|
+
// Aspect ratio specified but no size
|
|
171
|
+
const [widthRatio, heightRatio] = effectiveAspectRatio
|
|
172
|
+
.split(":")
|
|
173
|
+
.map(Number);
|
|
174
|
+
const targetRatio = widthRatio / heightRatio;
|
|
175
|
+
const viewportWidth = window.innerWidth;
|
|
176
|
+
const viewportHeight = window.innerHeight;
|
|
177
|
+
let targetWidth, targetHeight;
|
|
178
|
+
// Try fitting to viewport width first
|
|
179
|
+
targetWidth = viewportWidth;
|
|
180
|
+
targetHeight = targetWidth / targetRatio;
|
|
181
|
+
// If height exceeds viewport, fit to height instead
|
|
182
|
+
if (targetHeight > viewportHeight) {
|
|
183
|
+
targetHeight = viewportHeight;
|
|
184
|
+
targetWidth = targetHeight * targetRatio;
|
|
185
|
+
}
|
|
186
|
+
this.videoElement.width = targetWidth;
|
|
187
|
+
this.videoElement.height = targetHeight;
|
|
188
|
+
this.videoElement.style.width = `${targetWidth}px`;
|
|
189
|
+
this.videoElement.style.height = `${targetHeight}px`;
|
|
190
|
+
// Center the video element within its parent container
|
|
191
|
+
if (needsCenterX || options.x === undefined) {
|
|
192
|
+
const parentWidth = container.offsetWidth || viewportWidth;
|
|
193
|
+
const x = Math.round((parentWidth - targetWidth) / 2);
|
|
194
|
+
this.videoElement.style.left = `${x}px`;
|
|
195
|
+
}
|
|
196
|
+
if (needsCenterY || options.y === undefined) {
|
|
197
|
+
const parentHeight = container.offsetHeight || viewportHeight;
|
|
198
|
+
const y = Math.round((parentHeight - targetHeight) / 2);
|
|
199
|
+
this.videoElement.style.top = `${y}px`;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
100
202
|
this.videoElement.srcObject = stream;
|
|
101
203
|
if (!this.isBackCamera) {
|
|
102
204
|
this.videoElement.style.transform = "scaleX(-1)";
|
|
103
205
|
}
|
|
206
|
+
// Set initial zoom level if specified and supported
|
|
207
|
+
if (options.initialZoomLevel && options.initialZoomLevel !== 1.0) {
|
|
208
|
+
// videoTrack already declared above
|
|
209
|
+
if (videoTrack) {
|
|
210
|
+
const capabilities = videoTrack.getCapabilities();
|
|
211
|
+
if (capabilities.zoom) {
|
|
212
|
+
const zoomLevel = options.initialZoomLevel;
|
|
213
|
+
const minZoom = capabilities.zoom.min || 1;
|
|
214
|
+
const maxZoom = capabilities.zoom.max || 1;
|
|
215
|
+
if (zoomLevel < minZoom || zoomLevel > maxZoom) {
|
|
216
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
217
|
+
throw new Error(`Initial zoom level ${zoomLevel} is not available. Valid range is ${minZoom} to ${maxZoom}`);
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
await videoTrack.applyConstraints({
|
|
221
|
+
advanced: [{ zoom: zoomLevel }],
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
console.warn(`Failed to set initial zoom level: ${error}`);
|
|
226
|
+
// Don't throw, just continue without zoom
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
104
231
|
this.isStarted = true;
|
|
232
|
+
// Wait for video to be ready and get actual dimensions
|
|
233
|
+
await new Promise((resolve) => {
|
|
234
|
+
if (this.videoElement.readyState >= 2) {
|
|
235
|
+
resolve();
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
this.videoElement.addEventListener("loadeddata", () => resolve(), {
|
|
239
|
+
once: true,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
// Ensure centering is applied after DOM updates
|
|
244
|
+
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
245
|
+
console.log("About to re-center, flags:", { needsCenterX, needsCenterY });
|
|
246
|
+
// Re-apply centering with correct parent dimensions
|
|
247
|
+
if (needsCenterX) {
|
|
248
|
+
const parentWidth = container.offsetWidth;
|
|
249
|
+
const x = Math.round((parentWidth - this.videoElement.offsetWidth) / 2);
|
|
250
|
+
this.videoElement.style.left = `${x}px`;
|
|
251
|
+
console.log("Re-centering X:", {
|
|
252
|
+
parentWidth,
|
|
253
|
+
videoWidth: this.videoElement.offsetWidth,
|
|
254
|
+
x,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
if (needsCenterY) {
|
|
258
|
+
const y = Math.round((window.innerHeight - this.videoElement.offsetHeight) / 2);
|
|
259
|
+
this.videoElement.style.setProperty("top", `${y}px`, "important");
|
|
260
|
+
console.log("Re-centering Y:", {
|
|
261
|
+
viewportHeight: window.innerHeight,
|
|
262
|
+
videoHeight: this.videoElement.offsetHeight,
|
|
263
|
+
y,
|
|
264
|
+
position: this.videoElement.style.position,
|
|
265
|
+
top: this.videoElement.style.top,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
// Get the actual rendered dimensions after video is loaded
|
|
269
|
+
const rect = this.videoElement.getBoundingClientRect();
|
|
270
|
+
const computedStyle = window.getComputedStyle(this.videoElement);
|
|
271
|
+
console.log("Final video element state:", {
|
|
272
|
+
rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
|
|
273
|
+
style: {
|
|
274
|
+
position: computedStyle.position,
|
|
275
|
+
left: computedStyle.left,
|
|
276
|
+
top: computedStyle.top,
|
|
277
|
+
width: computedStyle.width,
|
|
278
|
+
height: computedStyle.height,
|
|
279
|
+
},
|
|
280
|
+
});
|
|
105
281
|
return {
|
|
106
|
-
width:
|
|
107
|
-
height:
|
|
108
|
-
x:
|
|
109
|
-
y:
|
|
282
|
+
width: Math.round(rect.width),
|
|
283
|
+
height: Math.round(rect.height),
|
|
284
|
+
x: Math.round(rect.x),
|
|
285
|
+
y: Math.round(rect.y),
|
|
110
286
|
};
|
|
111
287
|
}
|
|
112
288
|
stopStream(stream) {
|
|
@@ -140,14 +316,53 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
140
316
|
if (video && video.videoWidth > 0 && video.videoHeight > 0) {
|
|
141
317
|
const canvas = document.createElement("canvas");
|
|
142
318
|
const context = canvas.getContext("2d");
|
|
143
|
-
|
|
144
|
-
|
|
319
|
+
// Calculate capture dimensions
|
|
320
|
+
let captureWidth = video.videoWidth;
|
|
321
|
+
let captureHeight = video.videoHeight;
|
|
322
|
+
let sourceX = 0;
|
|
323
|
+
let sourceY = 0;
|
|
324
|
+
// Check for conflicting parameters
|
|
325
|
+
if (options.aspectRatio && (options.width || options.height)) {
|
|
326
|
+
reject(new Error("Cannot set both aspectRatio and size (width/height). Use setPreviewSize after start."));
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
// Handle aspect ratio if no width/height specified
|
|
330
|
+
if (!options.width && !options.height && options.aspectRatio) {
|
|
331
|
+
const [widthRatio, heightRatio] = options.aspectRatio.split(':').map(Number);
|
|
332
|
+
if (widthRatio && heightRatio) {
|
|
333
|
+
// For capture in portrait orientation, swap the aspect ratio (16:9 becomes 9:16)
|
|
334
|
+
const isPortrait = video.videoHeight > video.videoWidth;
|
|
335
|
+
const targetAspectRatio = isPortrait ? heightRatio / widthRatio : widthRatio / heightRatio;
|
|
336
|
+
const videoAspectRatio = video.videoWidth / video.videoHeight;
|
|
337
|
+
if (videoAspectRatio > targetAspectRatio) {
|
|
338
|
+
// Video is wider than target - crop sides
|
|
339
|
+
captureWidth = video.videoHeight * targetAspectRatio;
|
|
340
|
+
captureHeight = video.videoHeight;
|
|
341
|
+
sourceX = (video.videoWidth - captureWidth) / 2;
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
// Video is taller than target - crop top/bottom
|
|
345
|
+
captureWidth = video.videoWidth;
|
|
346
|
+
captureHeight = video.videoWidth / targetAspectRatio;
|
|
347
|
+
sourceY = (video.videoHeight - captureHeight) / 2;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
else if (options.width || options.height) {
|
|
352
|
+
// If width or height is specified, use them
|
|
353
|
+
if (options.width)
|
|
354
|
+
captureWidth = options.width;
|
|
355
|
+
if (options.height)
|
|
356
|
+
captureHeight = options.height;
|
|
357
|
+
}
|
|
358
|
+
canvas.width = captureWidth;
|
|
359
|
+
canvas.height = captureHeight;
|
|
145
360
|
// flip horizontally back camera isn't used
|
|
146
361
|
if (!this.isBackCamera) {
|
|
147
|
-
context === null || context === void 0 ? void 0 : context.translate(
|
|
362
|
+
context === null || context === void 0 ? void 0 : context.translate(captureWidth, 0);
|
|
148
363
|
context === null || context === void 0 ? void 0 : context.scale(-1, 1);
|
|
149
364
|
}
|
|
150
|
-
context === null || context === void 0 ? void 0 : context.drawImage(video, 0, 0,
|
|
365
|
+
context === null || context === void 0 ? void 0 : context.drawImage(video, sourceX, sourceY, captureWidth, captureHeight, 0, 0, captureWidth, captureHeight);
|
|
151
366
|
if (options.saveToGallery) {
|
|
152
367
|
// saveToGallery is not supported on web
|
|
153
368
|
}
|
|
@@ -383,6 +598,7 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
383
598
|
throw new Error("zoom not supported by this device");
|
|
384
599
|
}
|
|
385
600
|
const zoomLevel = Math.max(capabilities.zoom.min || 1, Math.min(capabilities.zoom.max || 1, options.level));
|
|
601
|
+
// Note: autoFocus is not supported on web platform
|
|
386
602
|
try {
|
|
387
603
|
await videoTrack.applyConstraints({
|
|
388
604
|
advanced: [{ zoom: zoomLevel }],
|
|
@@ -505,21 +721,25 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
505
721
|
video.style.left = `${x}px`;
|
|
506
722
|
video.style.top = `${y}px`;
|
|
507
723
|
video.style.position = "absolute";
|
|
724
|
+
const offsetX = newWidth / 8;
|
|
725
|
+
const offsetY = newHeight / 8;
|
|
508
726
|
return {
|
|
509
727
|
width: Math.round(newWidth),
|
|
510
728
|
height: Math.round(newHeight),
|
|
511
|
-
x: Math.round(x),
|
|
512
|
-
y: Math.round(y),
|
|
729
|
+
x: Math.round(x + offsetX),
|
|
730
|
+
y: Math.round(y + offsetY),
|
|
513
731
|
};
|
|
514
732
|
}
|
|
515
733
|
else {
|
|
516
734
|
video.style.objectFit = "cover";
|
|
517
735
|
const rect = video.getBoundingClientRect();
|
|
736
|
+
const offsetX = rect.width / 8;
|
|
737
|
+
const offsetY = rect.height / 8;
|
|
518
738
|
return {
|
|
519
739
|
width: Math.round(rect.width),
|
|
520
740
|
height: Math.round(rect.height),
|
|
521
|
-
x: Math.round(rect.left),
|
|
522
|
-
y: Math.round(rect.top),
|
|
741
|
+
x: Math.round(rect.left + offsetX),
|
|
742
|
+
y: Math.round(rect.top + offsetY),
|
|
523
743
|
};
|
|
524
744
|
}
|
|
525
745
|
}
|
|
@@ -578,9 +798,11 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
578
798
|
if (!video) {
|
|
579
799
|
throw new Error("camera is not running");
|
|
580
800
|
}
|
|
801
|
+
const offsetX = video.width / 8;
|
|
802
|
+
const offsetY = video.height / 8;
|
|
581
803
|
return {
|
|
582
|
-
x: video.offsetLeft,
|
|
583
|
-
y: video.offsetTop,
|
|
804
|
+
x: video.offsetLeft + offsetX,
|
|
805
|
+
y: video.offsetTop + offsetY,
|
|
584
806
|
width: video.width,
|
|
585
807
|
height: video.height,
|
|
586
808
|
};
|
|
@@ -594,14 +816,20 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
594
816
|
video.style.top = `${options.y}px`;
|
|
595
817
|
video.width = options.width;
|
|
596
818
|
video.height = options.height;
|
|
819
|
+
const offsetX = options.width / 8;
|
|
820
|
+
const offsetY = options.height / 8;
|
|
597
821
|
return {
|
|
598
822
|
width: options.width,
|
|
599
823
|
height: options.height,
|
|
600
|
-
x: options.x,
|
|
601
|
-
y: options.y,
|
|
824
|
+
x: options.x + offsetX,
|
|
825
|
+
y: options.y + offsetY,
|
|
602
826
|
};
|
|
603
827
|
}
|
|
604
828
|
async setFocus(options) {
|
|
829
|
+
// Reject if values are outside 0-1 range
|
|
830
|
+
if (options.x < 0 || options.x > 1 || options.y < 0 || options.y > 1) {
|
|
831
|
+
throw new Error("Focus coordinates must be between 0 and 1");
|
|
832
|
+
}
|
|
605
833
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
606
834
|
if (!(video === null || video === void 0 ? void 0 : video.srcObject)) {
|
|
607
835
|
throw new Error("camera is not running");
|