@capgo/camera-preview 7.4.0-beta.9 → 7.4.1
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 +246 -50
- package/android/gradle/wrapper/gradle-wrapper.properties +1 -1
- package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraPreview.java +1249 -143
- package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraXView.java +3400 -1432
- package/android/src/main/java/com/ahm/capacitor/camera/preview/GridOverlayView.java +95 -58
- 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 +160 -72
- 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 +443 -42
- package/dist/esm/definitions.d.ts +173 -27
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +24 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/web.d.ts +23 -3
- package/dist/esm/web.js +463 -65
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +485 -64
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +485 -64
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/{CapgoCameraPreview → CapgoCameraPreviewPlugin}/CameraController.swift +731 -315
- package/ios/Sources/CapgoCameraPreviewPlugin/Plugin.swift +1902 -0
- package/package.json +11 -3
- 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/fileChanges/last-build.bin +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/8.14.2/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
- package/android/.gradle/buildOutputCleanup/outputFiles.bin +0 -0
- package/android/.gradle/file-system.probe +0 -0
- package/android/.gradle/vcs-1/gc.properties +0 -0
- package/ios/Sources/CapgoCameraPreview/Plugin.swift +0 -1369
- /package/ios/Sources/{CapgoCameraPreview → CapgoCameraPreviewPlugin}/GridOverlayView.swift +0 -0
package/dist/plugin.js
CHANGED
|
@@ -15,6 +15,29 @@ var capacitorCapacitorCameraView = (function (exports, core) {
|
|
|
15
15
|
const CameraPreview = core.registerPlugin("CameraPreview", {
|
|
16
16
|
web: () => Promise.resolve().then(function () { return web; }).then((m) => new m.CameraPreviewWeb()),
|
|
17
17
|
});
|
|
18
|
+
async function getBase64FromFilePath(filePath) {
|
|
19
|
+
const url = core.Capacitor.convertFileSrc(filePath);
|
|
20
|
+
const response = await fetch(url);
|
|
21
|
+
if (!response.ok) {
|
|
22
|
+
throw new Error(`Failed to read file at path: ${filePath} (status ${response.status})`);
|
|
23
|
+
}
|
|
24
|
+
const blob = await response.blob();
|
|
25
|
+
return await new Promise((resolve, reject) => {
|
|
26
|
+
const reader = new FileReader();
|
|
27
|
+
reader.onloadend = () => {
|
|
28
|
+
const dataUrl = reader.result;
|
|
29
|
+
const commaIndex = dataUrl.indexOf(",");
|
|
30
|
+
resolve(commaIndex >= 0 ? dataUrl.substring(commaIndex + 1) : dataUrl);
|
|
31
|
+
};
|
|
32
|
+
reader.onerror = () => reject(reader.error);
|
|
33
|
+
reader.readAsDataURL(blob);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
async function deleteFile(path) {
|
|
37
|
+
// Use native bridge to delete file to handle platform-specific permissions/URIs
|
|
38
|
+
const { success } = await CameraPreview.deleteFile({ path });
|
|
39
|
+
return !!success;
|
|
40
|
+
}
|
|
18
41
|
|
|
19
42
|
const DEFAULT_VIDEO_ID = "capgo_video";
|
|
20
43
|
class CameraPreviewWeb extends core.WebPlugin {
|
|
@@ -28,6 +51,72 @@ var capacitorCapacitorCameraView = (function (exports, core) {
|
|
|
28
51
|
this.currentDeviceId = null;
|
|
29
52
|
this.videoElement = null;
|
|
30
53
|
this.isStarted = false;
|
|
54
|
+
this.orientationListenerBound = false;
|
|
55
|
+
}
|
|
56
|
+
getCurrentOrientation() {
|
|
57
|
+
var _a, _b;
|
|
58
|
+
try {
|
|
59
|
+
const so = screen.orientation;
|
|
60
|
+
const type = (so === null || so === void 0 ? void 0 : so.type) || (so === null || so === void 0 ? void 0 : so.mozOrientation) || (so === null || so === void 0 ? void 0 : so.msOrientation);
|
|
61
|
+
if (typeof type === "string") {
|
|
62
|
+
if (type.includes("portrait-primary"))
|
|
63
|
+
return "portrait";
|
|
64
|
+
if (type.includes("portrait-secondary"))
|
|
65
|
+
return "portrait-upside-down";
|
|
66
|
+
if (type.includes("landscape-primary"))
|
|
67
|
+
return "landscape-left";
|
|
68
|
+
if (type.includes("landscape-secondary"))
|
|
69
|
+
return "landscape-right";
|
|
70
|
+
if (type.includes("landscape"))
|
|
71
|
+
return "landscape";
|
|
72
|
+
if (type.includes("portrait"))
|
|
73
|
+
return "portrait";
|
|
74
|
+
}
|
|
75
|
+
const angle = window.orientation;
|
|
76
|
+
if (typeof angle === "number") {
|
|
77
|
+
if (angle === 0)
|
|
78
|
+
return "portrait";
|
|
79
|
+
if (angle === 180)
|
|
80
|
+
return "portrait-upside-down";
|
|
81
|
+
if (angle === 90)
|
|
82
|
+
return "landscape-right";
|
|
83
|
+
if (angle === -90)
|
|
84
|
+
return "landscape-left";
|
|
85
|
+
if (angle === 270)
|
|
86
|
+
return "landscape-left";
|
|
87
|
+
}
|
|
88
|
+
if ((_a = window.matchMedia("(orientation: portrait)")) === null || _a === void 0 ? void 0 : _a.matches) {
|
|
89
|
+
return "portrait";
|
|
90
|
+
}
|
|
91
|
+
if ((_b = window.matchMedia("(orientation: landscape)")) === null || _b === void 0 ? void 0 : _b.matches) {
|
|
92
|
+
return "landscape";
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch (e) {
|
|
96
|
+
console.error(e);
|
|
97
|
+
}
|
|
98
|
+
return "unknown";
|
|
99
|
+
}
|
|
100
|
+
ensureOrientationListener() {
|
|
101
|
+
if (this.orientationListenerBound)
|
|
102
|
+
return;
|
|
103
|
+
const emit = () => {
|
|
104
|
+
this.notifyListeners("orientationChange", {
|
|
105
|
+
orientation: this.getCurrentOrientation(),
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
window.addEventListener("orientationchange", emit);
|
|
109
|
+
window.addEventListener("resize", emit);
|
|
110
|
+
this.orientationListenerBound = true;
|
|
111
|
+
}
|
|
112
|
+
async getOrientation() {
|
|
113
|
+
return { orientation: this.getCurrentOrientation() };
|
|
114
|
+
}
|
|
115
|
+
getSafeAreaInsets() {
|
|
116
|
+
throw new Error("Method not implemented.");
|
|
117
|
+
}
|
|
118
|
+
async getZoomButtonValues() {
|
|
119
|
+
throw new Error("getZoomButtonValues not supported under the web platform");
|
|
31
120
|
}
|
|
32
121
|
async getSupportedPictureSizes() {
|
|
33
122
|
throw new Error("getSupportedPictureSizes not supported under the web platform");
|
|
@@ -43,6 +132,7 @@ var capacitorCapacitorCameraView = (function (exports, core) {
|
|
|
43
132
|
this.isStarted = false;
|
|
44
133
|
const parent = document.getElementById((options === null || options === void 0 ? void 0 : options.parent) || "");
|
|
45
134
|
const gridMode = (options === null || options === void 0 ? void 0 : options.gridMode) || "none";
|
|
135
|
+
const positioning = (options === null || options === void 0 ? void 0 : options.positioning) || "top";
|
|
46
136
|
if (options.position) {
|
|
47
137
|
this.isBackCamera = options.position === "rear";
|
|
48
138
|
}
|
|
@@ -50,7 +140,9 @@ var capacitorCapacitorCameraView = (function (exports, core) {
|
|
|
50
140
|
if (video) {
|
|
51
141
|
video.remove();
|
|
52
142
|
}
|
|
53
|
-
const container = options.parent
|
|
143
|
+
const container = options.parent
|
|
144
|
+
? document.getElementById(options.parent)
|
|
145
|
+
: document.body;
|
|
54
146
|
if (!container) {
|
|
55
147
|
throw new Error("container not found");
|
|
56
148
|
}
|
|
@@ -60,45 +152,57 @@ var capacitorCapacitorCameraView = (function (exports, core) {
|
|
|
60
152
|
this.videoElement.playsInline = true;
|
|
61
153
|
this.videoElement.muted = true;
|
|
62
154
|
this.videoElement.autoplay = true;
|
|
155
|
+
// Remove objectFit as we'll match camera's native aspect ratio
|
|
156
|
+
this.videoElement.style.backgroundColor = "transparent";
|
|
157
|
+
// Reset any default margins that might interfere
|
|
158
|
+
this.videoElement.style.margin = "0";
|
|
159
|
+
this.videoElement.style.padding = "0";
|
|
63
160
|
container.appendChild(this.videoElement);
|
|
64
161
|
if (options.toBack) {
|
|
65
162
|
this.videoElement.style.zIndex = "-1";
|
|
66
163
|
}
|
|
164
|
+
// Default to 4:3 if no aspect ratio or size specified
|
|
165
|
+
const useDefaultAspectRatio = !options.aspectRatio && !options.width && !options.height;
|
|
166
|
+
const effectiveAspectRatio = options.aspectRatio || (useDefaultAspectRatio ? "4:3" : null);
|
|
67
167
|
if (options.width) {
|
|
68
168
|
this.videoElement.width = options.width;
|
|
169
|
+
this.videoElement.style.width = `${options.width}px`;
|
|
69
170
|
}
|
|
70
171
|
if (options.height) {
|
|
71
172
|
this.videoElement.height = options.height;
|
|
173
|
+
this.videoElement.style.height = `${options.height}px`;
|
|
72
174
|
}
|
|
73
|
-
if
|
|
175
|
+
// Handle positioning - center if x or y not provided
|
|
176
|
+
const centerX = options.x === undefined;
|
|
177
|
+
const centerY = options.y === undefined;
|
|
178
|
+
// Always set position to absolute for proper positioning
|
|
179
|
+
this.videoElement.style.position = "absolute";
|
|
180
|
+
console.log("Initial positioning flags:", {
|
|
181
|
+
centerX,
|
|
182
|
+
centerY,
|
|
183
|
+
x: options.x,
|
|
184
|
+
y: options.y,
|
|
185
|
+
});
|
|
186
|
+
if (options.x !== undefined) {
|
|
74
187
|
this.videoElement.style.left = `${options.x}px`;
|
|
75
188
|
}
|
|
189
|
+
if (options.y !== undefined) {
|
|
190
|
+
this.videoElement.style.top = `${options.y}px`;
|
|
191
|
+
}
|
|
76
192
|
// Create and add grid overlay if needed
|
|
77
193
|
if (gridMode !== "none") {
|
|
78
194
|
const gridOverlay = this.createGridOverlay(gridMode);
|
|
79
195
|
gridOverlay.id = "camera-grid-overlay";
|
|
80
196
|
parent === null || parent === void 0 ? void 0 : parent.appendChild(gridOverlay);
|
|
81
197
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
if (options.width) {
|
|
89
|
-
this.videoElement.height = options.width / ratio;
|
|
90
|
-
}
|
|
91
|
-
else if (options.height) {
|
|
92
|
-
this.videoElement.width = options.height * ratio;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
else {
|
|
96
|
-
this.videoElement.style.objectFit = 'cover';
|
|
97
|
-
}
|
|
198
|
+
// Aspect ratio handling is now done after getting camera stream
|
|
199
|
+
// Store centering flags for later use
|
|
200
|
+
const needsCenterX = centerX;
|
|
201
|
+
const needsCenterY = centerY;
|
|
202
|
+
console.log("Centering flags stored:", { needsCenterX, needsCenterY });
|
|
203
|
+
// First get the camera stream with basic constraints
|
|
98
204
|
const constraints = {
|
|
99
205
|
video: {
|
|
100
|
-
width: { ideal: this.videoElement.width || 640 },
|
|
101
|
-
height: { ideal: this.videoElement.height || window.innerHeight },
|
|
102
206
|
facingMode: this.isBackCamera ? "environment" : "user",
|
|
103
207
|
},
|
|
104
208
|
};
|
|
@@ -109,16 +213,226 @@ var capacitorCapacitorCameraView = (function (exports, core) {
|
|
|
109
213
|
if (!this.videoElement) {
|
|
110
214
|
throw new Error("video element not found");
|
|
111
215
|
}
|
|
216
|
+
// Get the actual camera dimensions from the video track
|
|
217
|
+
const videoTrack = stream.getVideoTracks()[0];
|
|
218
|
+
const settings = videoTrack.getSettings();
|
|
219
|
+
const cameraWidth = settings.width || 640;
|
|
220
|
+
const cameraHeight = settings.height || 480;
|
|
221
|
+
const cameraAspectRatio = cameraWidth / cameraHeight;
|
|
222
|
+
console.log("Camera native dimensions:", {
|
|
223
|
+
width: cameraWidth,
|
|
224
|
+
height: cameraHeight,
|
|
225
|
+
aspectRatio: cameraAspectRatio,
|
|
226
|
+
});
|
|
227
|
+
console.log("Container dimensions:", {
|
|
228
|
+
width: container.offsetWidth,
|
|
229
|
+
height: container.offsetHeight,
|
|
230
|
+
id: container.id,
|
|
231
|
+
});
|
|
232
|
+
// Now adjust video element size based on camera's native aspect ratio
|
|
233
|
+
if (!options.width && !options.height && !options.aspectRatio) {
|
|
234
|
+
// No size specified, fit camera view within container bounds
|
|
235
|
+
const containerWidth = container.offsetWidth || window.innerWidth;
|
|
236
|
+
const containerHeight = container.offsetHeight || window.innerHeight;
|
|
237
|
+
// Calculate dimensions that fit within container while maintaining camera aspect ratio
|
|
238
|
+
let targetWidth, targetHeight;
|
|
239
|
+
// Try fitting to container width first
|
|
240
|
+
targetWidth = containerWidth;
|
|
241
|
+
targetHeight = targetWidth / cameraAspectRatio;
|
|
242
|
+
// If height exceeds container, fit to height instead
|
|
243
|
+
if (targetHeight > containerHeight) {
|
|
244
|
+
targetHeight = containerHeight;
|
|
245
|
+
targetWidth = targetHeight * cameraAspectRatio;
|
|
246
|
+
}
|
|
247
|
+
console.log("Video element dimensions:", {
|
|
248
|
+
width: targetWidth,
|
|
249
|
+
height: targetHeight,
|
|
250
|
+
container: { width: containerWidth, height: containerHeight },
|
|
251
|
+
});
|
|
252
|
+
this.videoElement.width = targetWidth;
|
|
253
|
+
this.videoElement.height = targetHeight;
|
|
254
|
+
this.videoElement.style.width = `${targetWidth}px`;
|
|
255
|
+
this.videoElement.style.height = `${targetHeight}px`;
|
|
256
|
+
// Center the video element within its parent container
|
|
257
|
+
if (needsCenterX || options.x === undefined) {
|
|
258
|
+
const x = Math.round((containerWidth - targetWidth) / 2);
|
|
259
|
+
this.videoElement.style.left = `${x}px`;
|
|
260
|
+
}
|
|
261
|
+
if (needsCenterY || options.y === undefined) {
|
|
262
|
+
let y;
|
|
263
|
+
switch (positioning) {
|
|
264
|
+
case "top":
|
|
265
|
+
y = 0;
|
|
266
|
+
break;
|
|
267
|
+
case "bottom":
|
|
268
|
+
y = window.innerHeight - targetHeight;
|
|
269
|
+
break;
|
|
270
|
+
case "center":
|
|
271
|
+
default:
|
|
272
|
+
y = Math.round((window.innerHeight - targetHeight) / 2);
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
this.videoElement.style.setProperty("top", `${y}px`, "important");
|
|
276
|
+
// Force a style recalculation
|
|
277
|
+
this.videoElement.offsetHeight;
|
|
278
|
+
console.log("Positioning video:", {
|
|
279
|
+
positioning,
|
|
280
|
+
viewportHeight: window.innerHeight,
|
|
281
|
+
targetHeight,
|
|
282
|
+
calculatedY: y,
|
|
283
|
+
actualTop: this.videoElement.style.top,
|
|
284
|
+
position: this.videoElement.style.position,
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
else if (effectiveAspectRatio && !options.width && !options.height) {
|
|
289
|
+
// Aspect ratio specified but no size
|
|
290
|
+
const [widthRatio, heightRatio] = effectiveAspectRatio
|
|
291
|
+
.split(":")
|
|
292
|
+
.map(Number);
|
|
293
|
+
const targetRatio = widthRatio / heightRatio;
|
|
294
|
+
const viewportWidth = window.innerWidth;
|
|
295
|
+
const viewportHeight = window.innerHeight;
|
|
296
|
+
let targetWidth, targetHeight;
|
|
297
|
+
// Try fitting to viewport width first
|
|
298
|
+
targetWidth = viewportWidth;
|
|
299
|
+
targetHeight = targetWidth / targetRatio;
|
|
300
|
+
// If height exceeds viewport, fit to height instead
|
|
301
|
+
if (targetHeight > viewportHeight) {
|
|
302
|
+
targetHeight = viewportHeight;
|
|
303
|
+
targetWidth = targetHeight * targetRatio;
|
|
304
|
+
}
|
|
305
|
+
this.videoElement.width = targetWidth;
|
|
306
|
+
this.videoElement.height = targetHeight;
|
|
307
|
+
this.videoElement.style.width = `${targetWidth}px`;
|
|
308
|
+
this.videoElement.style.height = `${targetHeight}px`;
|
|
309
|
+
// Center the video element within its parent container
|
|
310
|
+
if (needsCenterX || options.x === undefined) {
|
|
311
|
+
const parentWidth = container.offsetWidth || viewportWidth;
|
|
312
|
+
const x = Math.round((parentWidth - targetWidth) / 2);
|
|
313
|
+
this.videoElement.style.left = `${x}px`;
|
|
314
|
+
}
|
|
315
|
+
if (needsCenterY || options.y === undefined) {
|
|
316
|
+
const parentHeight = container.offsetHeight || viewportHeight;
|
|
317
|
+
let y;
|
|
318
|
+
switch (positioning) {
|
|
319
|
+
case "top":
|
|
320
|
+
y = 0;
|
|
321
|
+
break;
|
|
322
|
+
case "bottom":
|
|
323
|
+
y = parentHeight - targetHeight;
|
|
324
|
+
break;
|
|
325
|
+
case "center":
|
|
326
|
+
default:
|
|
327
|
+
y = Math.round((parentHeight - targetHeight) / 2);
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
this.videoElement.style.top = `${y}px`;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
112
333
|
this.videoElement.srcObject = stream;
|
|
113
334
|
if (!this.isBackCamera) {
|
|
114
335
|
this.videoElement.style.transform = "scaleX(-1)";
|
|
115
336
|
}
|
|
337
|
+
// Set initial zoom level if specified and supported
|
|
338
|
+
if (options.initialZoomLevel !== undefined &&
|
|
339
|
+
options.initialZoomLevel !== 1.0) {
|
|
340
|
+
// videoTrack already declared above
|
|
341
|
+
if (videoTrack) {
|
|
342
|
+
const capabilities = videoTrack.getCapabilities();
|
|
343
|
+
if (capabilities.zoom) {
|
|
344
|
+
const zoomLevel = options.initialZoomLevel;
|
|
345
|
+
const minZoom = capabilities.zoom.min || 1;
|
|
346
|
+
const maxZoom = capabilities.zoom.max || 1;
|
|
347
|
+
if (zoomLevel < minZoom || zoomLevel > maxZoom) {
|
|
348
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
349
|
+
throw new Error(`Initial zoom level ${zoomLevel} is not available. Valid range is ${minZoom} to ${maxZoom}`);
|
|
350
|
+
}
|
|
351
|
+
try {
|
|
352
|
+
await videoTrack.applyConstraints({
|
|
353
|
+
advanced: [{ zoom: zoomLevel }],
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
catch (error) {
|
|
357
|
+
console.warn(`Failed to set initial zoom level: ${error}`);
|
|
358
|
+
// Don't throw, just continue without zoom
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
116
363
|
this.isStarted = true;
|
|
364
|
+
this.ensureOrientationListener();
|
|
365
|
+
// Wait for video to be ready and get actual dimensions
|
|
366
|
+
await new Promise((resolve) => {
|
|
367
|
+
const videoEl = this.videoElement;
|
|
368
|
+
if (!videoEl) {
|
|
369
|
+
throw new Error("video element not found");
|
|
370
|
+
}
|
|
371
|
+
if (videoEl.readyState >= 2) {
|
|
372
|
+
resolve();
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
videoEl.addEventListener("loadeddata", () => resolve(), {
|
|
376
|
+
once: true,
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
// Ensure centering is applied after DOM updates
|
|
381
|
+
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
382
|
+
console.log("About to re-center, flags:", { needsCenterX, needsCenterY });
|
|
383
|
+
// Re-apply centering with correct parent dimensions
|
|
384
|
+
if (needsCenterX) {
|
|
385
|
+
const parentWidth = container.offsetWidth;
|
|
386
|
+
const x = Math.round((parentWidth - this.videoElement.offsetWidth) / 2);
|
|
387
|
+
this.videoElement.style.left = `${x}px`;
|
|
388
|
+
console.log("Re-centering X:", {
|
|
389
|
+
parentWidth,
|
|
390
|
+
videoWidth: this.videoElement.offsetWidth,
|
|
391
|
+
x,
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
if (needsCenterY) {
|
|
395
|
+
let y;
|
|
396
|
+
switch (positioning) {
|
|
397
|
+
case "top":
|
|
398
|
+
y = 0;
|
|
399
|
+
break;
|
|
400
|
+
case "bottom":
|
|
401
|
+
y = window.innerHeight - this.videoElement.offsetHeight;
|
|
402
|
+
break;
|
|
403
|
+
case "center":
|
|
404
|
+
default:
|
|
405
|
+
y = Math.round((window.innerHeight - this.videoElement.offsetHeight) / 2);
|
|
406
|
+
break;
|
|
407
|
+
}
|
|
408
|
+
this.videoElement.style.setProperty("top", `${y}px`, "important");
|
|
409
|
+
console.log("Re-positioning Y:", {
|
|
410
|
+
positioning,
|
|
411
|
+
viewportHeight: window.innerHeight,
|
|
412
|
+
videoHeight: this.videoElement.offsetHeight,
|
|
413
|
+
y,
|
|
414
|
+
position: this.videoElement.style.position,
|
|
415
|
+
top: this.videoElement.style.top,
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
// Get the actual rendered dimensions after video is loaded
|
|
419
|
+
const rect = this.videoElement.getBoundingClientRect();
|
|
420
|
+
const computedStyle = window.getComputedStyle(this.videoElement);
|
|
421
|
+
console.log("Final video element state:", {
|
|
422
|
+
rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
|
|
423
|
+
style: {
|
|
424
|
+
position: computedStyle.position,
|
|
425
|
+
left: computedStyle.left,
|
|
426
|
+
top: computedStyle.top,
|
|
427
|
+
width: computedStyle.width,
|
|
428
|
+
height: computedStyle.height,
|
|
429
|
+
},
|
|
430
|
+
});
|
|
117
431
|
return {
|
|
118
|
-
width:
|
|
119
|
-
height:
|
|
120
|
-
x:
|
|
121
|
-
y:
|
|
432
|
+
width: Math.round(rect.width),
|
|
433
|
+
height: Math.round(rect.height),
|
|
434
|
+
x: Math.round(rect.x),
|
|
435
|
+
y: Math.round(rect.y),
|
|
122
436
|
};
|
|
123
437
|
}
|
|
124
438
|
stopStream(stream) {
|
|
@@ -152,14 +466,57 @@ var capacitorCapacitorCameraView = (function (exports, core) {
|
|
|
152
466
|
if (video && video.videoWidth > 0 && video.videoHeight > 0) {
|
|
153
467
|
const canvas = document.createElement("canvas");
|
|
154
468
|
const context = canvas.getContext("2d");
|
|
155
|
-
|
|
156
|
-
|
|
469
|
+
// Calculate capture dimensions
|
|
470
|
+
let captureWidth = video.videoWidth;
|
|
471
|
+
let captureHeight = video.videoHeight;
|
|
472
|
+
let sourceX = 0;
|
|
473
|
+
let sourceY = 0;
|
|
474
|
+
// Check for conflicting parameters
|
|
475
|
+
if (options.aspectRatio && (options.width || options.height)) {
|
|
476
|
+
reject(new Error("Cannot set both aspectRatio and size (width/height). Use setPreviewSize after start."));
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
// Handle aspect ratio if no width/height specified
|
|
480
|
+
if (!options.width && !options.height && options.aspectRatio) {
|
|
481
|
+
const [widthRatio, heightRatio] = options.aspectRatio
|
|
482
|
+
.split(":")
|
|
483
|
+
.map(Number);
|
|
484
|
+
if (widthRatio && heightRatio) {
|
|
485
|
+
// For capture in portrait orientation, swap the aspect ratio (16:9 becomes 9:16)
|
|
486
|
+
const isPortrait = video.videoHeight > video.videoWidth;
|
|
487
|
+
const targetAspectRatio = isPortrait
|
|
488
|
+
? heightRatio / widthRatio
|
|
489
|
+
: widthRatio / heightRatio;
|
|
490
|
+
const videoAspectRatio = video.videoWidth / video.videoHeight;
|
|
491
|
+
if (videoAspectRatio > targetAspectRatio) {
|
|
492
|
+
// Video is wider than target - crop sides
|
|
493
|
+
captureWidth = video.videoHeight * targetAspectRatio;
|
|
494
|
+
captureHeight = video.videoHeight;
|
|
495
|
+
sourceX = (video.videoWidth - captureWidth) / 2;
|
|
496
|
+
}
|
|
497
|
+
else {
|
|
498
|
+
// Video is taller than target - crop top/bottom
|
|
499
|
+
captureWidth = video.videoWidth;
|
|
500
|
+
captureHeight = video.videoWidth / targetAspectRatio;
|
|
501
|
+
sourceY = (video.videoHeight - captureHeight) / 2;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
else if (options.width || options.height) {
|
|
506
|
+
// If width or height is specified, use them
|
|
507
|
+
if (options.width)
|
|
508
|
+
captureWidth = options.width;
|
|
509
|
+
if (options.height)
|
|
510
|
+
captureHeight = options.height;
|
|
511
|
+
}
|
|
512
|
+
canvas.width = captureWidth;
|
|
513
|
+
canvas.height = captureHeight;
|
|
157
514
|
// flip horizontally back camera isn't used
|
|
158
515
|
if (!this.isBackCamera) {
|
|
159
|
-
context === null || context === void 0 ? void 0 : context.translate(
|
|
516
|
+
context === null || context === void 0 ? void 0 : context.translate(captureWidth, 0);
|
|
160
517
|
context === null || context === void 0 ? void 0 : context.scale(-1, 1);
|
|
161
518
|
}
|
|
162
|
-
context === null || context === void 0 ? void 0 : context.drawImage(video, 0, 0,
|
|
519
|
+
context === null || context === void 0 ? void 0 : context.drawImage(video, sourceX, sourceY, captureWidth, captureHeight, 0, 0, captureWidth, captureHeight);
|
|
163
520
|
if (options.saveToGallery) ;
|
|
164
521
|
if (options.withExifLocation) ;
|
|
165
522
|
if ((options.format || "jpeg") === "jpeg") {
|
|
@@ -253,7 +610,7 @@ var capacitorCapacitorCameraView = (function (exports, core) {
|
|
|
253
610
|
throw new Error("getAvailableDevices not supported under the web platform");
|
|
254
611
|
}
|
|
255
612
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
256
|
-
const videoDevices = devices.filter(device => device.kind ===
|
|
613
|
+
const videoDevices = devices.filter((device) => device.kind === "videoinput");
|
|
257
614
|
// Group devices by position (front/back)
|
|
258
615
|
const frontDevices = [];
|
|
259
616
|
const backDevices = [];
|
|
@@ -263,15 +620,19 @@ var capacitorCapacitorCameraView = (function (exports, core) {
|
|
|
263
620
|
// Determine device type based on label
|
|
264
621
|
let deviceType = exports.DeviceType.WIDE_ANGLE;
|
|
265
622
|
let baseZoomRatio = 1.0;
|
|
266
|
-
if (labelLower.includes(
|
|
623
|
+
if (labelLower.includes("ultra") || labelLower.includes("0.5")) {
|
|
267
624
|
deviceType = exports.DeviceType.ULTRA_WIDE;
|
|
268
625
|
baseZoomRatio = 0.5;
|
|
269
626
|
}
|
|
270
|
-
else if (labelLower.includes(
|
|
627
|
+
else if (labelLower.includes("telephoto") ||
|
|
628
|
+
labelLower.includes("tele") ||
|
|
629
|
+
labelLower.includes("2x") ||
|
|
630
|
+
labelLower.includes("3x")) {
|
|
271
631
|
deviceType = exports.DeviceType.TELEPHOTO;
|
|
272
632
|
baseZoomRatio = 2.0;
|
|
273
633
|
}
|
|
274
|
-
else if (labelLower.includes(
|
|
634
|
+
else if (labelLower.includes("depth") ||
|
|
635
|
+
labelLower.includes("truedepth")) {
|
|
275
636
|
deviceType = exports.DeviceType.TRUE_DEPTH;
|
|
276
637
|
baseZoomRatio = 1.0;
|
|
277
638
|
}
|
|
@@ -282,10 +643,10 @@ var capacitorCapacitorCameraView = (function (exports, core) {
|
|
|
282
643
|
focalLength: 4.25,
|
|
283
644
|
baseZoomRatio,
|
|
284
645
|
minZoom: 1.0,
|
|
285
|
-
maxZoom: 1.0
|
|
646
|
+
maxZoom: 1.0,
|
|
286
647
|
};
|
|
287
648
|
// Determine position and add to appropriate array
|
|
288
|
-
if (labelLower.includes(
|
|
649
|
+
if (labelLower.includes("back") || labelLower.includes("rear")) {
|
|
289
650
|
backDevices.push(lensInfo);
|
|
290
651
|
}
|
|
291
652
|
else {
|
|
@@ -300,8 +661,8 @@ var capacitorCapacitorCameraView = (function (exports, core) {
|
|
|
300
661
|
position: "front",
|
|
301
662
|
lenses: frontDevices,
|
|
302
663
|
isLogical: false,
|
|
303
|
-
minZoom: Math.min(...frontDevices.map(d => d.minZoom)),
|
|
304
|
-
maxZoom: Math.max(...frontDevices.map(d => d.maxZoom))
|
|
664
|
+
minZoom: Math.min(...frontDevices.map((d) => d.minZoom)),
|
|
665
|
+
maxZoom: Math.max(...frontDevices.map((d) => d.maxZoom)),
|
|
305
666
|
});
|
|
306
667
|
}
|
|
307
668
|
if (backDevices.length > 0) {
|
|
@@ -311,8 +672,8 @@ var capacitorCapacitorCameraView = (function (exports, core) {
|
|
|
311
672
|
position: "rear",
|
|
312
673
|
lenses: backDevices,
|
|
313
674
|
isLogical: false,
|
|
314
|
-
minZoom: Math.min(...backDevices.map(d => d.minZoom)),
|
|
315
|
-
maxZoom: Math.max(...backDevices.map(d => d.maxZoom))
|
|
675
|
+
minZoom: Math.min(...backDevices.map((d) => d.minZoom)),
|
|
676
|
+
maxZoom: Math.max(...backDevices.map((d) => d.maxZoom)),
|
|
316
677
|
});
|
|
317
678
|
}
|
|
318
679
|
return { devices: result };
|
|
@@ -337,18 +698,22 @@ var capacitorCapacitorCameraView = (function (exports, core) {
|
|
|
337
698
|
let baseZoomRatio = 1.0;
|
|
338
699
|
if (this.currentDeviceId) {
|
|
339
700
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
340
|
-
const device = devices.find(d => d.deviceId === this.currentDeviceId);
|
|
701
|
+
const device = devices.find((d) => d.deviceId === this.currentDeviceId);
|
|
341
702
|
if (device) {
|
|
342
703
|
const labelLower = device.label.toLowerCase();
|
|
343
|
-
if (labelLower.includes(
|
|
704
|
+
if (labelLower.includes("ultra") || labelLower.includes("0.5")) {
|
|
344
705
|
deviceType = exports.DeviceType.ULTRA_WIDE;
|
|
345
706
|
baseZoomRatio = 0.5;
|
|
346
707
|
}
|
|
347
|
-
else if (labelLower.includes(
|
|
708
|
+
else if (labelLower.includes("telephoto") ||
|
|
709
|
+
labelLower.includes("tele") ||
|
|
710
|
+
labelLower.includes("2x") ||
|
|
711
|
+
labelLower.includes("3x")) {
|
|
348
712
|
deviceType = exports.DeviceType.TELEPHOTO;
|
|
349
713
|
baseZoomRatio = 2.0;
|
|
350
714
|
}
|
|
351
|
-
else if (labelLower.includes(
|
|
715
|
+
else if (labelLower.includes("depth") ||
|
|
716
|
+
labelLower.includes("truedepth")) {
|
|
352
717
|
deviceType = exports.DeviceType.TRUE_DEPTH;
|
|
353
718
|
baseZoomRatio = 1.0;
|
|
354
719
|
}
|
|
@@ -359,7 +724,7 @@ var capacitorCapacitorCameraView = (function (exports, core) {
|
|
|
359
724
|
focalLength: 4.25,
|
|
360
725
|
deviceType,
|
|
361
726
|
baseZoomRatio,
|
|
362
|
-
digitalZoom: currentZoom / baseZoomRatio
|
|
727
|
+
digitalZoom: currentZoom / baseZoomRatio,
|
|
363
728
|
};
|
|
364
729
|
return {
|
|
365
730
|
min: capabilities.zoom.min || 1,
|
|
@@ -383,9 +748,10 @@ var capacitorCapacitorCameraView = (function (exports, core) {
|
|
|
383
748
|
throw new Error("zoom not supported by this device");
|
|
384
749
|
}
|
|
385
750
|
const zoomLevel = Math.max(capabilities.zoom.min || 1, Math.min(capabilities.zoom.max || 1, options.level));
|
|
751
|
+
// Note: autoFocus is not supported on web platform
|
|
386
752
|
try {
|
|
387
753
|
await videoTrack.applyConstraints({
|
|
388
|
-
advanced: [{ zoom: zoomLevel }]
|
|
754
|
+
advanced: [{ zoom: zoomLevel }],
|
|
389
755
|
});
|
|
390
756
|
}
|
|
391
757
|
catch (error) {
|
|
@@ -418,8 +784,11 @@ var capacitorCapacitorCameraView = (function (exports, core) {
|
|
|
418
784
|
try {
|
|
419
785
|
// Try to determine camera position from device
|
|
420
786
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
421
|
-
const device = devices.find(d => d.deviceId === options.deviceId);
|
|
422
|
-
this.isBackCamera =
|
|
787
|
+
const device = devices.find((d) => d.deviceId === options.deviceId);
|
|
788
|
+
this.isBackCamera =
|
|
789
|
+
(device === null || device === void 0 ? void 0 : device.label.toLowerCase().includes("back")) ||
|
|
790
|
+
(device === null || device === void 0 ? void 0 : device.label.toLowerCase().includes("rear")) ||
|
|
791
|
+
false;
|
|
423
792
|
const stream = await navigator.mediaDevices.getUserMedia(constraints);
|
|
424
793
|
video.srcObject = stream;
|
|
425
794
|
// Update video transform based on camera
|
|
@@ -447,15 +816,15 @@ var capacitorCapacitorCameraView = (function (exports, core) {
|
|
|
447
816
|
if (width && height) {
|
|
448
817
|
const ratio = width / height;
|
|
449
818
|
// Check for portrait camera ratios: 4:3 -> 3:4, 16:9 -> 9:16
|
|
450
|
-
if (Math.abs(ratio -
|
|
451
|
-
return { aspectRatio:
|
|
819
|
+
if (Math.abs(ratio - 3 / 4) < 0.01) {
|
|
820
|
+
return { aspectRatio: "4:3" };
|
|
452
821
|
}
|
|
453
|
-
if (Math.abs(ratio -
|
|
454
|
-
return { aspectRatio:
|
|
822
|
+
if (Math.abs(ratio - 9 / 16) < 0.01) {
|
|
823
|
+
return { aspectRatio: "16:9" };
|
|
455
824
|
}
|
|
456
825
|
}
|
|
457
826
|
// Default to 4:3 if no specific aspect ratio is matched
|
|
458
|
-
return { aspectRatio:
|
|
827
|
+
return { aspectRatio: "4:3" };
|
|
459
828
|
}
|
|
460
829
|
async setAspectRatio(options) {
|
|
461
830
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
@@ -463,7 +832,9 @@ var capacitorCapacitorCameraView = (function (exports, core) {
|
|
|
463
832
|
throw new Error("camera is not running");
|
|
464
833
|
}
|
|
465
834
|
if (options.aspectRatio) {
|
|
466
|
-
const [widthRatio, heightRatio] = options.aspectRatio
|
|
835
|
+
const [widthRatio, heightRatio] = options.aspectRatio
|
|
836
|
+
.split(":")
|
|
837
|
+
.map(Number);
|
|
467
838
|
// For camera, use portrait orientation: 4:3 becomes 3:4, 16:9 becomes 9:16
|
|
468
839
|
const ratio = heightRatio / widthRatio;
|
|
469
840
|
// Get current position and size
|
|
@@ -499,22 +870,26 @@ var capacitorCapacitorCameraView = (function (exports, core) {
|
|
|
499
870
|
video.style.height = `${newHeight}px`;
|
|
500
871
|
video.style.left = `${x}px`;
|
|
501
872
|
video.style.top = `${y}px`;
|
|
502
|
-
video.style.position =
|
|
873
|
+
video.style.position = "absolute";
|
|
874
|
+
const offsetX = newWidth / 8;
|
|
875
|
+
const offsetY = newHeight / 8;
|
|
503
876
|
return {
|
|
504
877
|
width: Math.round(newWidth),
|
|
505
878
|
height: Math.round(newHeight),
|
|
506
|
-
x: Math.round(x),
|
|
507
|
-
y: Math.round(y)
|
|
879
|
+
x: Math.round(x + offsetX),
|
|
880
|
+
y: Math.round(y + offsetY),
|
|
508
881
|
};
|
|
509
882
|
}
|
|
510
883
|
else {
|
|
511
|
-
video.style.objectFit =
|
|
884
|
+
video.style.objectFit = "cover";
|
|
512
885
|
const rect = video.getBoundingClientRect();
|
|
886
|
+
const offsetX = rect.width / 8;
|
|
887
|
+
const offsetY = rect.height / 8;
|
|
513
888
|
return {
|
|
514
889
|
width: Math.round(rect.width),
|
|
515
890
|
height: Math.round(rect.height),
|
|
516
|
-
x: Math.round(rect.left),
|
|
517
|
-
y: Math.round(rect.top)
|
|
891
|
+
x: Math.round(rect.left + offsetX),
|
|
892
|
+
y: Math.round(rect.top + offsetY),
|
|
518
893
|
};
|
|
519
894
|
}
|
|
520
895
|
}
|
|
@@ -566,18 +941,20 @@ var capacitorCapacitorCameraView = (function (exports, core) {
|
|
|
566
941
|
}
|
|
567
942
|
async getGridMode() {
|
|
568
943
|
// Web implementation - default to none
|
|
569
|
-
return { gridMode:
|
|
944
|
+
return { gridMode: "none" };
|
|
570
945
|
}
|
|
571
946
|
async getPreviewSize() {
|
|
572
947
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
573
948
|
if (!video) {
|
|
574
949
|
throw new Error("camera is not running");
|
|
575
950
|
}
|
|
951
|
+
const offsetX = video.width / 8;
|
|
952
|
+
const offsetY = video.height / 8;
|
|
576
953
|
return {
|
|
577
|
-
x: video.offsetLeft,
|
|
578
|
-
y: video.offsetTop,
|
|
954
|
+
x: video.offsetLeft + offsetX,
|
|
955
|
+
y: video.offsetTop + offsetY,
|
|
579
956
|
width: video.width,
|
|
580
|
-
height: video.height
|
|
957
|
+
height: video.height,
|
|
581
958
|
};
|
|
582
959
|
}
|
|
583
960
|
async setPreviewSize(options) {
|
|
@@ -589,13 +966,55 @@ var capacitorCapacitorCameraView = (function (exports, core) {
|
|
|
589
966
|
video.style.top = `${options.y}px`;
|
|
590
967
|
video.width = options.width;
|
|
591
968
|
video.height = options.height;
|
|
969
|
+
const offsetX = options.width / 8;
|
|
970
|
+
const offsetY = options.height / 8;
|
|
592
971
|
return {
|
|
593
972
|
width: options.width,
|
|
594
973
|
height: options.height,
|
|
595
|
-
x: options.x,
|
|
596
|
-
y: options.y
|
|
974
|
+
x: options.x + offsetX,
|
|
975
|
+
y: options.y + offsetY,
|
|
597
976
|
};
|
|
598
977
|
}
|
|
978
|
+
async setFocus(options) {
|
|
979
|
+
// Reject if values are outside 0-1 range
|
|
980
|
+
if (options.x < 0 || options.x > 1 || options.y < 0 || options.y > 1) {
|
|
981
|
+
throw new Error("Focus coordinates must be between 0 and 1");
|
|
982
|
+
}
|
|
983
|
+
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
984
|
+
if (!(video === null || video === void 0 ? void 0 : video.srcObject)) {
|
|
985
|
+
throw new Error("camera is not running");
|
|
986
|
+
}
|
|
987
|
+
const stream = video.srcObject;
|
|
988
|
+
const videoTrack = stream.getVideoTracks()[0];
|
|
989
|
+
if (!videoTrack) {
|
|
990
|
+
throw new Error("no video track found");
|
|
991
|
+
}
|
|
992
|
+
const capabilities = videoTrack.getCapabilities();
|
|
993
|
+
// Check if focusing is supported
|
|
994
|
+
if (capabilities.focusMode) {
|
|
995
|
+
try {
|
|
996
|
+
// Web API supports focus mode settings but not coordinate-based focus
|
|
997
|
+
// Setting to manual mode allows for coordinate focus if supported
|
|
998
|
+
await videoTrack.applyConstraints({
|
|
999
|
+
advanced: [
|
|
1000
|
+
{
|
|
1001
|
+
focusMode: "manual",
|
|
1002
|
+
focusDistance: 0.5, // Mid-range focus as fallback
|
|
1003
|
+
},
|
|
1004
|
+
],
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
catch (error) {
|
|
1008
|
+
console.warn(`setFocus is not fully supported on this device: ${error}. Focus coordinates (${options.x}, ${options.y}) were provided but cannot be applied.`);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
else {
|
|
1012
|
+
console.warn("Focus control is not supported on this device. Focus coordinates were provided but cannot be applied.");
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
async deleteFile(_options) {
|
|
1016
|
+
throw new Error("deleteFile not supported under the web platform");
|
|
1017
|
+
}
|
|
599
1018
|
}
|
|
600
1019
|
|
|
601
1020
|
var web = /*#__PURE__*/Object.freeze({
|
|
@@ -604,6 +1023,8 @@ var capacitorCapacitorCameraView = (function (exports, core) {
|
|
|
604
1023
|
});
|
|
605
1024
|
|
|
606
1025
|
exports.CameraPreview = CameraPreview;
|
|
1026
|
+
exports.deleteFile = deleteFile;
|
|
1027
|
+
exports.getBase64FromFilePath = getBase64FromFilePath;
|
|
607
1028
|
|
|
608
1029
|
return exports;
|
|
609
1030
|
|