@capgo/camera-preview 7.23.10 → 7.23.12
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 +18 -18
- package/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java +6 -6
- package/android/src/main/java/app/capgo/capacitor/camera/preview/CameraPreview.java +1838 -2291
- package/android/src/main/java/app/capgo/capacitor/camera/preview/CameraXView.java +3153 -3948
- package/android/src/main/java/app/capgo/capacitor/camera/preview/GridOverlayView.java +86 -97
- package/android/src/main/java/app/capgo/capacitor/camera/preview/model/CameraDevice.java +53 -53
- package/android/src/main/java/app/capgo/capacitor/camera/preview/model/CameraSessionConfiguration.java +166 -166
- package/android/src/main/java/app/capgo/capacitor/camera/preview/model/LensInfo.java +22 -27
- package/android/src/main/java/app/capgo/capacitor/camera/preview/model/ZoomFactors.java +22 -22
- package/android/src/test/java/com/getcapacitor/ExampleUnitTest.java +4 -4
- package/dist/docs.json +34 -34
- package/dist/esm/definitions.d.ts +15 -15
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +5 -5
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/web.d.ts +4 -4
- package/dist/esm/web.js +201 -216
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +201 -216
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +201 -216
- package/dist/plugin.js.map +1 -1
- package/package.json +2 -2
package/dist/plugin.cjs.js
CHANGED
|
@@ -13,7 +13,7 @@ exports.DeviceType = void 0;
|
|
|
13
13
|
DeviceType["TRIPLE"] = "triple";
|
|
14
14
|
})(exports.DeviceType || (exports.DeviceType = {}));
|
|
15
15
|
|
|
16
|
-
const CameraPreview = core.registerPlugin(
|
|
16
|
+
const CameraPreview = core.registerPlugin('CameraPreview', {
|
|
17
17
|
web: () => Promise.resolve().then(function () { return web; }).then((m) => new m.CameraPreviewWeb()),
|
|
18
18
|
});
|
|
19
19
|
async function getBase64FromFilePath(filePath) {
|
|
@@ -27,7 +27,7 @@ async function getBase64FromFilePath(filePath) {
|
|
|
27
27
|
const reader = new FileReader();
|
|
28
28
|
reader.onloadend = () => {
|
|
29
29
|
const dataUrl = reader.result;
|
|
30
|
-
const commaIndex = dataUrl.indexOf(
|
|
30
|
+
const commaIndex = dataUrl.indexOf(',');
|
|
31
31
|
resolve(commaIndex >= 0 ? dataUrl.substring(commaIndex + 1) : dataUrl);
|
|
32
32
|
};
|
|
33
33
|
reader.onerror = () => reject(reader.error);
|
|
@@ -40,7 +40,7 @@ async function deleteFile(path) {
|
|
|
40
40
|
return !!success;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
const DEFAULT_VIDEO_ID =
|
|
43
|
+
const DEFAULT_VIDEO_ID = 'capgo_video';
|
|
44
44
|
class CameraPreviewWeb extends core.WebPlugin {
|
|
45
45
|
constructor() {
|
|
46
46
|
super();
|
|
@@ -56,32 +56,32 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
56
56
|
}
|
|
57
57
|
async checkPermissions(options) {
|
|
58
58
|
const result = {
|
|
59
|
-
camera:
|
|
59
|
+
camera: 'prompt',
|
|
60
60
|
};
|
|
61
61
|
const permissionsApi = navigator === null || navigator === void 0 ? void 0 : navigator.permissions;
|
|
62
62
|
if (permissionsApi === null || permissionsApi === void 0 ? void 0 : permissionsApi.query) {
|
|
63
63
|
try {
|
|
64
|
-
const cameraPermission = await permissionsApi.query({ name:
|
|
64
|
+
const cameraPermission = await permissionsApi.query({ name: 'camera' });
|
|
65
65
|
result.camera = this.mapWebPermission(cameraPermission.state);
|
|
66
66
|
}
|
|
67
67
|
catch (error) {
|
|
68
|
-
console.warn(
|
|
68
|
+
console.warn('Camera permission query failed', error);
|
|
69
69
|
}
|
|
70
70
|
if ((options === null || options === void 0 ? void 0 : options.disableAudio) === false) {
|
|
71
71
|
try {
|
|
72
72
|
const microphonePermission = await permissionsApi.query({
|
|
73
|
-
name:
|
|
73
|
+
name: 'microphone',
|
|
74
74
|
});
|
|
75
75
|
result.microphone = this.mapWebPermission(microphonePermission.state);
|
|
76
76
|
}
|
|
77
77
|
catch (error) {
|
|
78
|
-
console.warn(
|
|
79
|
-
result.microphone =
|
|
78
|
+
console.warn('Microphone permission query failed', error);
|
|
79
|
+
result.microphone = 'prompt';
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
else if ((options === null || options === void 0 ? void 0 : options.disableAudio) === false) {
|
|
84
|
-
result.microphone =
|
|
84
|
+
result.microphone = 'prompt';
|
|
85
85
|
}
|
|
86
86
|
return result;
|
|
87
87
|
}
|
|
@@ -89,15 +89,13 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
89
89
|
var _a, _b;
|
|
90
90
|
const disableAudio = (_a = options === null || options === void 0 ? void 0 : options.disableAudio) !== null && _a !== void 0 ? _a : true;
|
|
91
91
|
if ((_b = navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices) === null || _b === void 0 ? void 0 : _b.getUserMedia) {
|
|
92
|
-
const constraints = disableAudio
|
|
93
|
-
? { video: true }
|
|
94
|
-
: { video: true, audio: true };
|
|
92
|
+
const constraints = disableAudio ? { video: true } : { video: true, audio: true };
|
|
95
93
|
let stream;
|
|
96
94
|
try {
|
|
97
95
|
stream = await navigator.mediaDevices.getUserMedia(constraints);
|
|
98
96
|
}
|
|
99
97
|
catch (error) {
|
|
100
|
-
console.warn(
|
|
98
|
+
console.warn('Unable to obtain camera or microphone stream', error);
|
|
101
99
|
}
|
|
102
100
|
finally {
|
|
103
101
|
try {
|
|
@@ -110,19 +108,19 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
110
108
|
}
|
|
111
109
|
const status = await this.checkPermissions({ disableAudio: disableAudio });
|
|
112
110
|
if (options === null || options === void 0 ? void 0 : options.showSettingsAlert) {
|
|
113
|
-
console.warn(
|
|
111
|
+
console.warn('showSettingsAlert is not supported on the web platform; returning permission status only.');
|
|
114
112
|
}
|
|
115
113
|
return status;
|
|
116
114
|
}
|
|
117
115
|
mapWebPermission(state) {
|
|
118
116
|
switch (state) {
|
|
119
|
-
case
|
|
120
|
-
return
|
|
121
|
-
case
|
|
122
|
-
return
|
|
123
|
-
case
|
|
117
|
+
case 'granted':
|
|
118
|
+
return 'granted';
|
|
119
|
+
case 'denied':
|
|
120
|
+
return 'denied';
|
|
121
|
+
case 'prompt':
|
|
124
122
|
default:
|
|
125
|
-
return
|
|
123
|
+
return 'prompt';
|
|
126
124
|
}
|
|
127
125
|
}
|
|
128
126
|
getCurrentOrientation() {
|
|
@@ -130,113 +128,111 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
130
128
|
try {
|
|
131
129
|
const so = screen.orientation;
|
|
132
130
|
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);
|
|
133
|
-
if (typeof type ===
|
|
134
|
-
if (type.includes(
|
|
135
|
-
return
|
|
136
|
-
if (type.includes(
|
|
137
|
-
return
|
|
138
|
-
if (type.includes(
|
|
139
|
-
return
|
|
140
|
-
if (type.includes(
|
|
141
|
-
return
|
|
142
|
-
if (type.includes(
|
|
143
|
-
return
|
|
144
|
-
if (type.includes(
|
|
145
|
-
return
|
|
131
|
+
if (typeof type === 'string') {
|
|
132
|
+
if (type.includes('portrait-primary'))
|
|
133
|
+
return 'portrait';
|
|
134
|
+
if (type.includes('portrait-secondary'))
|
|
135
|
+
return 'portrait-upside-down';
|
|
136
|
+
if (type.includes('landscape-primary'))
|
|
137
|
+
return 'landscape-left';
|
|
138
|
+
if (type.includes('landscape-secondary'))
|
|
139
|
+
return 'landscape-right';
|
|
140
|
+
if (type.includes('landscape'))
|
|
141
|
+
return 'landscape-right'; // avoid generic landscape
|
|
142
|
+
if (type.includes('portrait'))
|
|
143
|
+
return 'portrait';
|
|
146
144
|
}
|
|
147
145
|
const angle = window.orientation;
|
|
148
|
-
if (typeof angle ===
|
|
146
|
+
if (typeof angle === 'number') {
|
|
149
147
|
if (angle === 0)
|
|
150
|
-
return
|
|
148
|
+
return 'portrait';
|
|
151
149
|
if (angle === 180)
|
|
152
|
-
return
|
|
150
|
+
return 'portrait-upside-down';
|
|
153
151
|
if (angle === 90)
|
|
154
|
-
return
|
|
152
|
+
return 'landscape-right';
|
|
155
153
|
if (angle === -90)
|
|
156
|
-
return
|
|
154
|
+
return 'landscape-left';
|
|
157
155
|
if (angle === 270)
|
|
158
|
-
return
|
|
156
|
+
return 'landscape-left';
|
|
159
157
|
}
|
|
160
|
-
if ((_a = window.matchMedia(
|
|
161
|
-
return
|
|
158
|
+
if ((_a = window.matchMedia('(orientation: portrait)')) === null || _a === void 0 ? void 0 : _a.matches) {
|
|
159
|
+
return 'portrait';
|
|
162
160
|
}
|
|
163
|
-
if ((_b = window.matchMedia(
|
|
161
|
+
if ((_b = window.matchMedia('(orientation: landscape)')) === null || _b === void 0 ? void 0 : _b.matches) {
|
|
164
162
|
// Default to landscape-right when we can't distinguish primary/secondary
|
|
165
|
-
return
|
|
163
|
+
return 'landscape-right';
|
|
166
164
|
}
|
|
167
165
|
}
|
|
168
166
|
catch (e) {
|
|
169
167
|
console.error(e);
|
|
170
168
|
}
|
|
171
|
-
return
|
|
169
|
+
return 'unknown';
|
|
172
170
|
}
|
|
173
171
|
ensureOrientationListener() {
|
|
174
172
|
if (this.orientationListenerBound)
|
|
175
173
|
return;
|
|
176
174
|
const emit = () => {
|
|
177
|
-
this.notifyListeners(
|
|
175
|
+
this.notifyListeners('orientationChange', {
|
|
178
176
|
orientation: this.getCurrentOrientation(),
|
|
179
177
|
});
|
|
180
178
|
};
|
|
181
|
-
window.addEventListener(
|
|
182
|
-
window.addEventListener(
|
|
179
|
+
window.addEventListener('orientationchange', emit);
|
|
180
|
+
window.addEventListener('resize', emit);
|
|
183
181
|
this.orientationListenerBound = true;
|
|
184
182
|
}
|
|
185
183
|
async getOrientation() {
|
|
186
184
|
return { orientation: this.getCurrentOrientation() };
|
|
187
185
|
}
|
|
188
186
|
getSafeAreaInsets() {
|
|
189
|
-
throw new Error(
|
|
187
|
+
throw new Error('Method not implemented.');
|
|
190
188
|
}
|
|
191
189
|
async getZoomButtonValues() {
|
|
192
|
-
throw new Error(
|
|
190
|
+
throw new Error('getZoomButtonValues not supported under the web platform');
|
|
193
191
|
}
|
|
194
192
|
async getSupportedPictureSizes() {
|
|
195
|
-
throw new Error(
|
|
193
|
+
throw new Error('getSupportedPictureSizes not supported under the web platform');
|
|
196
194
|
}
|
|
197
195
|
async start(options) {
|
|
198
196
|
if (options.aspectRatio && (options.width || options.height)) {
|
|
199
|
-
throw new Error(
|
|
197
|
+
throw new Error('Cannot set both aspectRatio and size (width/height). Use setPreviewSize after start.');
|
|
200
198
|
}
|
|
201
199
|
if (this.isStarted) {
|
|
202
|
-
throw new Error(
|
|
200
|
+
throw new Error('camera already started');
|
|
203
201
|
}
|
|
204
202
|
this.isBackCamera = true;
|
|
205
203
|
this.isStarted = false;
|
|
206
|
-
const parent = document.getElementById((options === null || options === void 0 ? void 0 : options.parent) ||
|
|
207
|
-
const gridMode = (options === null || options === void 0 ? void 0 : options.gridMode) ||
|
|
208
|
-
const positioning = (options === null || options === void 0 ? void 0 : options.positioning) ||
|
|
204
|
+
const parent = document.getElementById((options === null || options === void 0 ? void 0 : options.parent) || '');
|
|
205
|
+
const gridMode = (options === null || options === void 0 ? void 0 : options.gridMode) || 'none';
|
|
206
|
+
const positioning = (options === null || options === void 0 ? void 0 : options.positioning) || 'top';
|
|
209
207
|
if (options.position) {
|
|
210
|
-
this.isBackCamera = options.position ===
|
|
208
|
+
this.isBackCamera = options.position === 'rear';
|
|
211
209
|
}
|
|
212
210
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
213
211
|
if (video) {
|
|
214
212
|
video.remove();
|
|
215
213
|
}
|
|
216
|
-
const container = options.parent
|
|
217
|
-
? document.getElementById(options.parent)
|
|
218
|
-
: document.body;
|
|
214
|
+
const container = options.parent ? document.getElementById(options.parent) : document.body;
|
|
219
215
|
if (!container) {
|
|
220
|
-
throw new Error(
|
|
216
|
+
throw new Error('container not found');
|
|
221
217
|
}
|
|
222
|
-
this.videoElement = document.createElement(
|
|
218
|
+
this.videoElement = document.createElement('video');
|
|
223
219
|
this.videoElement.id = DEFAULT_VIDEO_ID;
|
|
224
|
-
this.videoElement.className = options.className ||
|
|
220
|
+
this.videoElement.className = options.className || '';
|
|
225
221
|
this.videoElement.playsInline = true;
|
|
226
222
|
this.videoElement.muted = true;
|
|
227
223
|
this.videoElement.autoplay = true;
|
|
228
224
|
// Remove objectFit as we'll match camera's native aspect ratio
|
|
229
|
-
this.videoElement.style.backgroundColor =
|
|
225
|
+
this.videoElement.style.backgroundColor = 'transparent';
|
|
230
226
|
// Reset any default margins that might interfere
|
|
231
|
-
this.videoElement.style.margin =
|
|
232
|
-
this.videoElement.style.padding =
|
|
227
|
+
this.videoElement.style.margin = '0';
|
|
228
|
+
this.videoElement.style.padding = '0';
|
|
233
229
|
container.appendChild(this.videoElement);
|
|
234
230
|
if (options.toBack) {
|
|
235
|
-
this.videoElement.style.zIndex =
|
|
231
|
+
this.videoElement.style.zIndex = '-1';
|
|
236
232
|
}
|
|
237
233
|
// Default to 4:3 if no aspect ratio or size specified
|
|
238
234
|
const useDefaultAspectRatio = !options.aspectRatio && !options.width && !options.height;
|
|
239
|
-
const effectiveAspectRatio = options.aspectRatio || (useDefaultAspectRatio ?
|
|
235
|
+
const effectiveAspectRatio = options.aspectRatio || (useDefaultAspectRatio ? '4:3' : null);
|
|
240
236
|
if (options.width) {
|
|
241
237
|
this.videoElement.width = options.width;
|
|
242
238
|
this.videoElement.style.width = `${options.width}px`;
|
|
@@ -249,8 +245,8 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
249
245
|
const centerX = options.x === undefined;
|
|
250
246
|
const centerY = options.y === undefined;
|
|
251
247
|
// Always set position to absolute for proper positioning
|
|
252
|
-
this.videoElement.style.position =
|
|
253
|
-
console.log(
|
|
248
|
+
this.videoElement.style.position = 'absolute';
|
|
249
|
+
console.log('Initial positioning flags:', {
|
|
254
250
|
centerX,
|
|
255
251
|
centerY,
|
|
256
252
|
x: options.x,
|
|
@@ -263,28 +259,28 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
263
259
|
this.videoElement.style.top = `${options.y}px`;
|
|
264
260
|
}
|
|
265
261
|
// Create and add grid overlay if needed
|
|
266
|
-
if (gridMode !==
|
|
262
|
+
if (gridMode !== 'none') {
|
|
267
263
|
const gridOverlay = this.createGridOverlay(gridMode);
|
|
268
|
-
gridOverlay.id =
|
|
264
|
+
gridOverlay.id = 'camera-grid-overlay';
|
|
269
265
|
parent === null || parent === void 0 ? void 0 : parent.appendChild(gridOverlay);
|
|
270
266
|
}
|
|
271
267
|
// Aspect ratio handling is now done after getting camera stream
|
|
272
268
|
// Store centering flags for later use
|
|
273
269
|
const needsCenterX = centerX;
|
|
274
270
|
const needsCenterY = centerY;
|
|
275
|
-
console.log(
|
|
271
|
+
console.log('Centering flags stored:', { needsCenterX, needsCenterY });
|
|
276
272
|
// First get the camera stream with basic constraints
|
|
277
273
|
const constraints = {
|
|
278
274
|
video: {
|
|
279
|
-
facingMode: this.isBackCamera ?
|
|
275
|
+
facingMode: this.isBackCamera ? 'environment' : 'user',
|
|
280
276
|
},
|
|
281
277
|
};
|
|
282
278
|
const stream = await navigator.mediaDevices.getUserMedia(constraints);
|
|
283
279
|
if (!stream) {
|
|
284
|
-
throw new Error(
|
|
280
|
+
throw new Error('could not acquire stream');
|
|
285
281
|
}
|
|
286
282
|
if (!this.videoElement) {
|
|
287
|
-
throw new Error(
|
|
283
|
+
throw new Error('video element not found');
|
|
288
284
|
}
|
|
289
285
|
// Get the actual camera dimensions from the video track
|
|
290
286
|
const videoTrack = stream.getVideoTracks()[0];
|
|
@@ -292,12 +288,12 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
292
288
|
const cameraWidth = settings.width || 640;
|
|
293
289
|
const cameraHeight = settings.height || 480;
|
|
294
290
|
const cameraAspectRatio = cameraWidth / cameraHeight;
|
|
295
|
-
console.log(
|
|
291
|
+
console.log('Camera native dimensions:', {
|
|
296
292
|
width: cameraWidth,
|
|
297
293
|
height: cameraHeight,
|
|
298
294
|
aspectRatio: cameraAspectRatio,
|
|
299
295
|
});
|
|
300
|
-
console.log(
|
|
296
|
+
console.log('Container dimensions:', {
|
|
301
297
|
width: container.offsetWidth,
|
|
302
298
|
height: container.offsetHeight,
|
|
303
299
|
id: container.id,
|
|
@@ -317,7 +313,7 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
317
313
|
targetHeight = containerHeight;
|
|
318
314
|
targetWidth = targetHeight * cameraAspectRatio;
|
|
319
315
|
}
|
|
320
|
-
console.log(
|
|
316
|
+
console.log('Video element dimensions:', {
|
|
321
317
|
width: targetWidth,
|
|
322
318
|
height: targetHeight,
|
|
323
319
|
container: { width: containerWidth, height: containerHeight },
|
|
@@ -334,21 +330,21 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
334
330
|
if (needsCenterY || options.y === undefined) {
|
|
335
331
|
let y;
|
|
336
332
|
switch (positioning) {
|
|
337
|
-
case
|
|
333
|
+
case 'top':
|
|
338
334
|
y = 0;
|
|
339
335
|
break;
|
|
340
|
-
case
|
|
336
|
+
case 'bottom':
|
|
341
337
|
y = window.innerHeight - targetHeight;
|
|
342
338
|
break;
|
|
343
|
-
case
|
|
339
|
+
case 'center':
|
|
344
340
|
default:
|
|
345
341
|
y = Math.round((window.innerHeight - targetHeight) / 2);
|
|
346
342
|
break;
|
|
347
343
|
}
|
|
348
|
-
this.videoElement.style.setProperty(
|
|
344
|
+
this.videoElement.style.setProperty('top', `${y}px`, 'important');
|
|
349
345
|
// Force a style recalculation
|
|
350
346
|
this.videoElement.offsetHeight;
|
|
351
|
-
console.log(
|
|
347
|
+
console.log('Positioning video:', {
|
|
352
348
|
positioning,
|
|
353
349
|
viewportHeight: window.innerHeight,
|
|
354
350
|
targetHeight,
|
|
@@ -360,9 +356,7 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
360
356
|
}
|
|
361
357
|
else if (effectiveAspectRatio && !options.width && !options.height) {
|
|
362
358
|
// Aspect ratio specified but no size
|
|
363
|
-
const [widthRatio, heightRatio] = effectiveAspectRatio
|
|
364
|
-
.split(":")
|
|
365
|
-
.map(Number);
|
|
359
|
+
const [widthRatio, heightRatio] = effectiveAspectRatio.split(':').map(Number);
|
|
366
360
|
const targetRatio = widthRatio / heightRatio;
|
|
367
361
|
const viewportWidth = window.innerWidth;
|
|
368
362
|
const viewportHeight = window.innerHeight;
|
|
@@ -389,13 +383,13 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
389
383
|
const parentHeight = container.offsetHeight || viewportHeight;
|
|
390
384
|
let y;
|
|
391
385
|
switch (positioning) {
|
|
392
|
-
case
|
|
386
|
+
case 'top':
|
|
393
387
|
y = 0;
|
|
394
388
|
break;
|
|
395
|
-
case
|
|
389
|
+
case 'bottom':
|
|
396
390
|
y = parentHeight - targetHeight;
|
|
397
391
|
break;
|
|
398
|
-
case
|
|
392
|
+
case 'center':
|
|
399
393
|
default:
|
|
400
394
|
y = Math.round((parentHeight - targetHeight) / 2);
|
|
401
395
|
break;
|
|
@@ -405,11 +399,10 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
405
399
|
}
|
|
406
400
|
this.videoElement.srcObject = stream;
|
|
407
401
|
if (!this.isBackCamera) {
|
|
408
|
-
this.videoElement.style.transform =
|
|
402
|
+
this.videoElement.style.transform = 'scaleX(-1)';
|
|
409
403
|
}
|
|
410
404
|
// Set initial zoom level if specified and supported
|
|
411
|
-
if (options.initialZoomLevel !== undefined &&
|
|
412
|
-
options.initialZoomLevel !== 1.0) {
|
|
405
|
+
if (options.initialZoomLevel !== undefined && options.initialZoomLevel !== 1.0) {
|
|
413
406
|
// videoTrack already declared above
|
|
414
407
|
if (videoTrack) {
|
|
415
408
|
const capabilities = videoTrack.getCapabilities();
|
|
@@ -439,26 +432,26 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
439
432
|
await new Promise((resolve) => {
|
|
440
433
|
const videoEl = this.videoElement;
|
|
441
434
|
if (!videoEl) {
|
|
442
|
-
throw new Error(
|
|
435
|
+
throw new Error('video element not found');
|
|
443
436
|
}
|
|
444
437
|
if (videoEl.readyState >= 2) {
|
|
445
438
|
resolve();
|
|
446
439
|
}
|
|
447
440
|
else {
|
|
448
|
-
videoEl.addEventListener(
|
|
441
|
+
videoEl.addEventListener('loadeddata', () => resolve(), {
|
|
449
442
|
once: true,
|
|
450
443
|
});
|
|
451
444
|
}
|
|
452
445
|
});
|
|
453
446
|
// Ensure centering is applied after DOM updates
|
|
454
447
|
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
455
|
-
console.log(
|
|
448
|
+
console.log('About to re-center, flags:', { needsCenterX, needsCenterY });
|
|
456
449
|
// Re-apply centering with correct parent dimensions
|
|
457
450
|
if (needsCenterX) {
|
|
458
451
|
const parentWidth = container.offsetWidth;
|
|
459
452
|
const x = Math.round((parentWidth - this.videoElement.offsetWidth) / 2);
|
|
460
453
|
this.videoElement.style.left = `${x}px`;
|
|
461
|
-
console.log(
|
|
454
|
+
console.log('Re-centering X:', {
|
|
462
455
|
parentWidth,
|
|
463
456
|
videoWidth: this.videoElement.offsetWidth,
|
|
464
457
|
x,
|
|
@@ -467,19 +460,19 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
467
460
|
if (needsCenterY) {
|
|
468
461
|
let y;
|
|
469
462
|
switch (positioning) {
|
|
470
|
-
case
|
|
463
|
+
case 'top':
|
|
471
464
|
y = 0;
|
|
472
465
|
break;
|
|
473
|
-
case
|
|
466
|
+
case 'bottom':
|
|
474
467
|
y = window.innerHeight - this.videoElement.offsetHeight;
|
|
475
468
|
break;
|
|
476
|
-
case
|
|
469
|
+
case 'center':
|
|
477
470
|
default:
|
|
478
471
|
y = Math.round((window.innerHeight - this.videoElement.offsetHeight) / 2);
|
|
479
472
|
break;
|
|
480
473
|
}
|
|
481
|
-
this.videoElement.style.setProperty(
|
|
482
|
-
console.log(
|
|
474
|
+
this.videoElement.style.setProperty('top', `${y}px`, 'important');
|
|
475
|
+
console.log('Re-positioning Y:', {
|
|
483
476
|
positioning,
|
|
484
477
|
viewportHeight: window.innerHeight,
|
|
485
478
|
videoHeight: this.videoElement.offsetHeight,
|
|
@@ -491,7 +484,7 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
491
484
|
// Get the actual rendered dimensions after video is loaded
|
|
492
485
|
const rect = this.videoElement.getBoundingClientRect();
|
|
493
486
|
const computedStyle = window.getComputedStyle(this.videoElement);
|
|
494
|
-
console.log(
|
|
487
|
+
console.log('Final video element state:', {
|
|
495
488
|
rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
|
|
496
489
|
style: {
|
|
497
490
|
position: computedStyle.position,
|
|
@@ -524,21 +517,21 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
524
517
|
this.isStarted = false;
|
|
525
518
|
}
|
|
526
519
|
// Remove grid overlay if it exists
|
|
527
|
-
const gridOverlay = document.getElementById(
|
|
520
|
+
const gridOverlay = document.getElementById('camera-grid-overlay');
|
|
528
521
|
gridOverlay === null || gridOverlay === void 0 ? void 0 : gridOverlay.remove();
|
|
529
522
|
}
|
|
530
523
|
async capture(options) {
|
|
531
524
|
return new Promise((resolve, reject) => {
|
|
532
525
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
533
526
|
if (!(video === null || video === void 0 ? void 0 : video.srcObject)) {
|
|
534
|
-
reject(new Error(
|
|
527
|
+
reject(new Error('camera is not running'));
|
|
535
528
|
return;
|
|
536
529
|
}
|
|
537
530
|
// video.width = video.offsetWidth;
|
|
538
531
|
let base64EncodedImage;
|
|
539
532
|
if (video && video.videoWidth > 0 && video.videoHeight > 0) {
|
|
540
|
-
const canvas = document.createElement(
|
|
541
|
-
const context = canvas.getContext(
|
|
533
|
+
const canvas = document.createElement('canvas');
|
|
534
|
+
const context = canvas.getContext('2d');
|
|
542
535
|
// Calculate capture dimensions
|
|
543
536
|
let captureWidth = video.videoWidth;
|
|
544
537
|
let captureHeight = video.videoHeight;
|
|
@@ -585,15 +578,13 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
585
578
|
context === null || context === void 0 ? void 0 : context.drawImage(video, sourceX, sourceY, captureWidth, captureHeight, 0, 0, captureWidth, captureHeight);
|
|
586
579
|
if (options.saveToGallery) ;
|
|
587
580
|
if (options.withExifLocation) ;
|
|
588
|
-
if ((options.format ||
|
|
581
|
+
if ((options.format || 'jpeg') === 'jpeg') {
|
|
589
582
|
base64EncodedImage = canvas
|
|
590
|
-
.toDataURL(
|
|
591
|
-
.replace(
|
|
583
|
+
.toDataURL('image/jpeg', (options.quality || 85) / 100.0)
|
|
584
|
+
.replace('data:image/jpeg;base64,', '');
|
|
592
585
|
}
|
|
593
586
|
else {
|
|
594
|
-
base64EncodedImage = canvas
|
|
595
|
-
.toDataURL("image/png")
|
|
596
|
-
.replace("data:image/png;base64,", "");
|
|
587
|
+
base64EncodedImage = canvas.toDataURL('image/png').replace('data:image/png;base64,', '');
|
|
597
588
|
}
|
|
598
589
|
}
|
|
599
590
|
resolve({
|
|
@@ -606,17 +597,17 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
606
597
|
return this.capture(_options);
|
|
607
598
|
}
|
|
608
599
|
async stopRecordVideo() {
|
|
609
|
-
throw new Error(
|
|
600
|
+
throw new Error('stopRecordVideo not supported under the web platform');
|
|
610
601
|
}
|
|
611
602
|
async startRecordVideo(_options) {
|
|
612
|
-
console.log(
|
|
613
|
-
throw new Error(
|
|
603
|
+
console.log('startRecordVideo', _options);
|
|
604
|
+
throw new Error('startRecordVideo not supported under the web platform');
|
|
614
605
|
}
|
|
615
606
|
async getSupportedFlashModes() {
|
|
616
|
-
throw new Error(
|
|
607
|
+
throw new Error('getSupportedFlashModes not supported under the web platform');
|
|
617
608
|
}
|
|
618
609
|
async getHorizontalFov() {
|
|
619
|
-
throw new Error(
|
|
610
|
+
throw new Error('getHorizontalFov not supported under the web platform');
|
|
620
611
|
}
|
|
621
612
|
async setFlashMode(_options) {
|
|
622
613
|
throw new Error(`setFlashMode not supported under the web platform${_options}`);
|
|
@@ -624,7 +615,7 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
624
615
|
async flip() {
|
|
625
616
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
626
617
|
if (!(video === null || video === void 0 ? void 0 : video.srcObject)) {
|
|
627
|
-
throw new Error(
|
|
618
|
+
throw new Error('camera is not running');
|
|
628
619
|
}
|
|
629
620
|
// Stop current stream
|
|
630
621
|
this.stopStream(video.srcObject);
|
|
@@ -633,7 +624,7 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
633
624
|
// Get new constraints
|
|
634
625
|
const constraints = {
|
|
635
626
|
video: {
|
|
636
|
-
facingMode: this.isBackCamera ?
|
|
627
|
+
facingMode: this.isBackCamera ? 'environment' : 'user',
|
|
637
628
|
width: { ideal: video.videoWidth || 640 },
|
|
638
629
|
height: { ideal: video.videoHeight || 480 },
|
|
639
630
|
},
|
|
@@ -648,12 +639,12 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
648
639
|
}
|
|
649
640
|
// Update video transform based on camera
|
|
650
641
|
if (this.isBackCamera) {
|
|
651
|
-
video.style.transform =
|
|
652
|
-
video.style.webkitTransform =
|
|
642
|
+
video.style.transform = 'none';
|
|
643
|
+
video.style.webkitTransform = 'none';
|
|
653
644
|
}
|
|
654
645
|
else {
|
|
655
|
-
video.style.transform =
|
|
656
|
-
video.style.webkitTransform =
|
|
646
|
+
video.style.transform = 'scaleX(-1)';
|
|
647
|
+
video.style.webkitTransform = 'scaleX(-1)';
|
|
657
648
|
}
|
|
658
649
|
await video.play();
|
|
659
650
|
}
|
|
@@ -664,7 +655,7 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
664
655
|
async setOpacity(_options) {
|
|
665
656
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
666
657
|
if (!!video && !!_options.opacity)
|
|
667
|
-
video.style.setProperty(
|
|
658
|
+
video.style.setProperty('opacity', _options.opacity.toString());
|
|
668
659
|
}
|
|
669
660
|
async isRunning() {
|
|
670
661
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
@@ -673,10 +664,10 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
673
664
|
async getAvailableDevices() {
|
|
674
665
|
var _a;
|
|
675
666
|
if (!((_a = navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.enumerateDevices)) {
|
|
676
|
-
throw new Error(
|
|
667
|
+
throw new Error('getAvailableDevices not supported under the web platform');
|
|
677
668
|
}
|
|
678
669
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
679
|
-
const videoDevices = devices.filter((device) => device.kind ===
|
|
670
|
+
const videoDevices = devices.filter((device) => device.kind === 'videoinput');
|
|
680
671
|
// Group devices by position (front/back)
|
|
681
672
|
const frontDevices = [];
|
|
682
673
|
const backDevices = [];
|
|
@@ -686,19 +677,18 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
686
677
|
// Determine device type based on label
|
|
687
678
|
let deviceType = exports.DeviceType.WIDE_ANGLE;
|
|
688
679
|
let baseZoomRatio = 1.0;
|
|
689
|
-
if (labelLower.includes(
|
|
680
|
+
if (labelLower.includes('ultra') || labelLower.includes('0.5')) {
|
|
690
681
|
deviceType = exports.DeviceType.ULTRA_WIDE;
|
|
691
682
|
baseZoomRatio = 0.5;
|
|
692
683
|
}
|
|
693
|
-
else if (labelLower.includes(
|
|
694
|
-
labelLower.includes(
|
|
695
|
-
labelLower.includes(
|
|
696
|
-
labelLower.includes(
|
|
684
|
+
else if (labelLower.includes('telephoto') ||
|
|
685
|
+
labelLower.includes('tele') ||
|
|
686
|
+
labelLower.includes('2x') ||
|
|
687
|
+
labelLower.includes('3x')) {
|
|
697
688
|
deviceType = exports.DeviceType.TELEPHOTO;
|
|
698
689
|
baseZoomRatio = 2.0;
|
|
699
690
|
}
|
|
700
|
-
else if (labelLower.includes(
|
|
701
|
-
labelLower.includes("truedepth")) {
|
|
691
|
+
else if (labelLower.includes('depth') || labelLower.includes('truedepth')) {
|
|
702
692
|
deviceType = exports.DeviceType.TRUE_DEPTH;
|
|
703
693
|
baseZoomRatio = 1.0;
|
|
704
694
|
}
|
|
@@ -712,7 +702,7 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
712
702
|
maxZoom: 1.0,
|
|
713
703
|
};
|
|
714
704
|
// Determine position and add to appropriate array
|
|
715
|
-
if (labelLower.includes(
|
|
705
|
+
if (labelLower.includes('back') || labelLower.includes('rear')) {
|
|
716
706
|
backDevices.push(lensInfo);
|
|
717
707
|
}
|
|
718
708
|
else {
|
|
@@ -723,8 +713,8 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
723
713
|
if (frontDevices.length > 0) {
|
|
724
714
|
result.push({
|
|
725
715
|
deviceId: frontDevices[0].deviceId,
|
|
726
|
-
label:
|
|
727
|
-
position:
|
|
716
|
+
label: 'Front Camera',
|
|
717
|
+
position: 'front',
|
|
728
718
|
lenses: frontDevices,
|
|
729
719
|
isLogical: false,
|
|
730
720
|
minZoom: Math.min(...frontDevices.map((d) => d.minZoom)),
|
|
@@ -734,8 +724,8 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
734
724
|
if (backDevices.length > 0) {
|
|
735
725
|
result.push({
|
|
736
726
|
deviceId: backDevices[0].deviceId,
|
|
737
|
-
label:
|
|
738
|
-
position:
|
|
727
|
+
label: 'Back Camera',
|
|
728
|
+
position: 'rear',
|
|
739
729
|
lenses: backDevices,
|
|
740
730
|
isLogical: false,
|
|
741
731
|
minZoom: Math.min(...backDevices.map((d) => d.minZoom)),
|
|
@@ -747,17 +737,17 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
747
737
|
async getZoom() {
|
|
748
738
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
749
739
|
if (!(video === null || video === void 0 ? void 0 : video.srcObject)) {
|
|
750
|
-
throw new Error(
|
|
740
|
+
throw new Error('camera is not running');
|
|
751
741
|
}
|
|
752
742
|
const stream = video.srcObject;
|
|
753
743
|
const videoTrack = stream.getVideoTracks()[0];
|
|
754
744
|
if (!videoTrack) {
|
|
755
|
-
throw new Error(
|
|
745
|
+
throw new Error('no video track found');
|
|
756
746
|
}
|
|
757
747
|
const capabilities = videoTrack.getCapabilities();
|
|
758
748
|
const settings = videoTrack.getSettings();
|
|
759
749
|
if (!capabilities.zoom) {
|
|
760
|
-
throw new Error(
|
|
750
|
+
throw new Error('zoom not supported by this device');
|
|
761
751
|
}
|
|
762
752
|
// Get current device info to determine lens type
|
|
763
753
|
let deviceType = exports.DeviceType.WIDE_ANGLE;
|
|
@@ -767,19 +757,18 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
767
757
|
const device = devices.find((d) => d.deviceId === this.currentDeviceId);
|
|
768
758
|
if (device) {
|
|
769
759
|
const labelLower = device.label.toLowerCase();
|
|
770
|
-
if (labelLower.includes(
|
|
760
|
+
if (labelLower.includes('ultra') || labelLower.includes('0.5')) {
|
|
771
761
|
deviceType = exports.DeviceType.ULTRA_WIDE;
|
|
772
762
|
baseZoomRatio = 0.5;
|
|
773
763
|
}
|
|
774
|
-
else if (labelLower.includes(
|
|
775
|
-
labelLower.includes(
|
|
776
|
-
labelLower.includes(
|
|
777
|
-
labelLower.includes(
|
|
764
|
+
else if (labelLower.includes('telephoto') ||
|
|
765
|
+
labelLower.includes('tele') ||
|
|
766
|
+
labelLower.includes('2x') ||
|
|
767
|
+
labelLower.includes('3x')) {
|
|
778
768
|
deviceType = exports.DeviceType.TELEPHOTO;
|
|
779
769
|
baseZoomRatio = 2.0;
|
|
780
770
|
}
|
|
781
|
-
else if (labelLower.includes(
|
|
782
|
-
labelLower.includes("truedepth")) {
|
|
771
|
+
else if (labelLower.includes('depth') || labelLower.includes('truedepth')) {
|
|
783
772
|
deviceType = exports.DeviceType.TRUE_DEPTH;
|
|
784
773
|
baseZoomRatio = 1.0;
|
|
785
774
|
}
|
|
@@ -802,16 +791,16 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
802
791
|
async setZoom(options) {
|
|
803
792
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
804
793
|
if (!(video === null || video === void 0 ? void 0 : video.srcObject)) {
|
|
805
|
-
throw new Error(
|
|
794
|
+
throw new Error('camera is not running');
|
|
806
795
|
}
|
|
807
796
|
const stream = video.srcObject;
|
|
808
797
|
const videoTrack = stream.getVideoTracks()[0];
|
|
809
798
|
if (!videoTrack) {
|
|
810
|
-
throw new Error(
|
|
799
|
+
throw new Error('no video track found');
|
|
811
800
|
}
|
|
812
801
|
const capabilities = videoTrack.getCapabilities();
|
|
813
802
|
if (!capabilities.zoom) {
|
|
814
|
-
throw new Error(
|
|
803
|
+
throw new Error('zoom not supported by this device');
|
|
815
804
|
}
|
|
816
805
|
const zoomLevel = Math.max(capabilities.zoom.min || 1, Math.min(capabilities.zoom.max || 1, options.level));
|
|
817
806
|
// Note: autoFocus is not supported on web platform
|
|
@@ -825,15 +814,15 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
825
814
|
}
|
|
826
815
|
}
|
|
827
816
|
async getFlashMode() {
|
|
828
|
-
throw new Error(
|
|
817
|
+
throw new Error('getFlashMode not supported under the web platform');
|
|
829
818
|
}
|
|
830
819
|
async getDeviceId() {
|
|
831
|
-
return { deviceId: this.currentDeviceId ||
|
|
820
|
+
return { deviceId: this.currentDeviceId || '' };
|
|
832
821
|
}
|
|
833
822
|
async setDeviceId(options) {
|
|
834
823
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
835
824
|
if (!(video === null || video === void 0 ? void 0 : video.srcObject)) {
|
|
836
|
-
throw new Error(
|
|
825
|
+
throw new Error('camera is not running');
|
|
837
826
|
}
|
|
838
827
|
// Stop current stream
|
|
839
828
|
this.stopStream(video.srcObject);
|
|
@@ -852,19 +841,17 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
852
841
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
853
842
|
const device = devices.find((d) => d.deviceId === options.deviceId);
|
|
854
843
|
this.isBackCamera =
|
|
855
|
-
(device === null || device === void 0 ? void 0 : device.label.toLowerCase().includes(
|
|
856
|
-
(device === null || device === void 0 ? void 0 : device.label.toLowerCase().includes("rear")) ||
|
|
857
|
-
false;
|
|
844
|
+
(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;
|
|
858
845
|
const stream = await navigator.mediaDevices.getUserMedia(constraints);
|
|
859
846
|
video.srcObject = stream;
|
|
860
847
|
// Update video transform based on camera
|
|
861
848
|
if (this.isBackCamera) {
|
|
862
|
-
video.style.transform =
|
|
863
|
-
video.style.webkitTransform =
|
|
849
|
+
video.style.transform = 'none';
|
|
850
|
+
video.style.webkitTransform = 'none';
|
|
864
851
|
}
|
|
865
852
|
else {
|
|
866
|
-
video.style.transform =
|
|
867
|
-
video.style.webkitTransform =
|
|
853
|
+
video.style.transform = 'scaleX(-1)';
|
|
854
|
+
video.style.webkitTransform = 'scaleX(-1)';
|
|
868
855
|
}
|
|
869
856
|
await video.play();
|
|
870
857
|
}
|
|
@@ -875,7 +862,7 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
875
862
|
async getAspectRatio() {
|
|
876
863
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
877
864
|
if (!video) {
|
|
878
|
-
throw new Error(
|
|
865
|
+
throw new Error('camera is not running');
|
|
879
866
|
}
|
|
880
867
|
const width = video.offsetWidth;
|
|
881
868
|
const height = video.offsetHeight;
|
|
@@ -883,24 +870,22 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
883
870
|
const ratio = width / height;
|
|
884
871
|
// Check for portrait camera ratios: 4:3 -> 3:4, 16:9 -> 9:16
|
|
885
872
|
if (Math.abs(ratio - 3 / 4) < 0.01) {
|
|
886
|
-
return { aspectRatio:
|
|
873
|
+
return { aspectRatio: '4:3' };
|
|
887
874
|
}
|
|
888
875
|
if (Math.abs(ratio - 9 / 16) < 0.01) {
|
|
889
|
-
return { aspectRatio:
|
|
876
|
+
return { aspectRatio: '16:9' };
|
|
890
877
|
}
|
|
891
878
|
}
|
|
892
879
|
// Default to 4:3 if no specific aspect ratio is matched
|
|
893
|
-
return { aspectRatio:
|
|
880
|
+
return { aspectRatio: '4:3' };
|
|
894
881
|
}
|
|
895
882
|
async setAspectRatio(options) {
|
|
896
883
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
897
884
|
if (!video) {
|
|
898
|
-
throw new Error(
|
|
885
|
+
throw new Error('camera is not running');
|
|
899
886
|
}
|
|
900
887
|
if (options.aspectRatio) {
|
|
901
|
-
const [widthRatio, heightRatio] = options.aspectRatio
|
|
902
|
-
.split(":")
|
|
903
|
-
.map(Number);
|
|
888
|
+
const [widthRatio, heightRatio] = options.aspectRatio.split(':').map(Number);
|
|
904
889
|
// For camera, use portrait orientation: 4:3 becomes 3:4, 16:9 becomes 9:16
|
|
905
890
|
const ratio = heightRatio / widthRatio;
|
|
906
891
|
// Get current position and size
|
|
@@ -936,7 +921,7 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
936
921
|
video.style.height = `${newHeight}px`;
|
|
937
922
|
video.style.left = `${x}px`;
|
|
938
923
|
video.style.top = `${y}px`;
|
|
939
|
-
video.style.position =
|
|
924
|
+
video.style.position = 'absolute';
|
|
940
925
|
const offsetX = newWidth / 8;
|
|
941
926
|
const offsetY = newHeight / 8;
|
|
942
927
|
return {
|
|
@@ -947,7 +932,7 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
947
932
|
};
|
|
948
933
|
}
|
|
949
934
|
else {
|
|
950
|
-
video.style.objectFit =
|
|
935
|
+
video.style.objectFit = 'cover';
|
|
951
936
|
const rect = video.getBoundingClientRect();
|
|
952
937
|
const offsetX = rect.width / 8;
|
|
953
938
|
const offsetY = rect.height / 8;
|
|
@@ -960,41 +945,41 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
960
945
|
}
|
|
961
946
|
}
|
|
962
947
|
createGridOverlay(gridMode) {
|
|
963
|
-
const overlay = document.createElement(
|
|
964
|
-
overlay.style.position =
|
|
965
|
-
overlay.style.top =
|
|
966
|
-
overlay.style.left =
|
|
967
|
-
overlay.style.width =
|
|
968
|
-
overlay.style.height =
|
|
969
|
-
overlay.style.pointerEvents =
|
|
970
|
-
overlay.style.zIndex =
|
|
971
|
-
const divisions = gridMode ===
|
|
948
|
+
const overlay = document.createElement('div');
|
|
949
|
+
overlay.style.position = 'absolute';
|
|
950
|
+
overlay.style.top = '0';
|
|
951
|
+
overlay.style.left = '0';
|
|
952
|
+
overlay.style.width = '100%';
|
|
953
|
+
overlay.style.height = '100%';
|
|
954
|
+
overlay.style.pointerEvents = 'none';
|
|
955
|
+
overlay.style.zIndex = '10';
|
|
956
|
+
const divisions = gridMode === '3x3' ? 3 : 4;
|
|
972
957
|
// Create SVG for grid lines
|
|
973
|
-
const svg = document.createElementNS(
|
|
974
|
-
svg.style.width =
|
|
975
|
-
svg.style.height =
|
|
976
|
-
svg.style.position =
|
|
977
|
-
svg.style.top =
|
|
978
|
-
svg.style.left =
|
|
958
|
+
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
959
|
+
svg.style.width = '100%';
|
|
960
|
+
svg.style.height = '100%';
|
|
961
|
+
svg.style.position = 'absolute';
|
|
962
|
+
svg.style.top = '0';
|
|
963
|
+
svg.style.left = '0';
|
|
979
964
|
// Create grid lines
|
|
980
965
|
for (let i = 1; i < divisions; i++) {
|
|
981
966
|
// Vertical lines
|
|
982
|
-
const verticalLine = document.createElementNS(
|
|
983
|
-
verticalLine.setAttribute(
|
|
984
|
-
verticalLine.setAttribute(
|
|
985
|
-
verticalLine.setAttribute(
|
|
986
|
-
verticalLine.setAttribute(
|
|
987
|
-
verticalLine.setAttribute(
|
|
988
|
-
verticalLine.setAttribute(
|
|
967
|
+
const verticalLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');
|
|
968
|
+
verticalLine.setAttribute('x1', `${(i / divisions) * 100}%`);
|
|
969
|
+
verticalLine.setAttribute('y1', '0%');
|
|
970
|
+
verticalLine.setAttribute('x2', `${(i / divisions) * 100}%`);
|
|
971
|
+
verticalLine.setAttribute('y2', '100%');
|
|
972
|
+
verticalLine.setAttribute('stroke', 'rgba(255, 255, 255, 0.5)');
|
|
973
|
+
verticalLine.setAttribute('stroke-width', '1');
|
|
989
974
|
svg.appendChild(verticalLine);
|
|
990
975
|
// Horizontal lines
|
|
991
|
-
const horizontalLine = document.createElementNS(
|
|
992
|
-
horizontalLine.setAttribute(
|
|
993
|
-
horizontalLine.setAttribute(
|
|
994
|
-
horizontalLine.setAttribute(
|
|
995
|
-
horizontalLine.setAttribute(
|
|
996
|
-
horizontalLine.setAttribute(
|
|
997
|
-
horizontalLine.setAttribute(
|
|
976
|
+
const horizontalLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');
|
|
977
|
+
horizontalLine.setAttribute('x1', '0%');
|
|
978
|
+
horizontalLine.setAttribute('y1', `${(i / divisions) * 100}%`);
|
|
979
|
+
horizontalLine.setAttribute('x2', '100%');
|
|
980
|
+
horizontalLine.setAttribute('y2', `${(i / divisions) * 100}%`);
|
|
981
|
+
horizontalLine.setAttribute('stroke', 'rgba(255, 255, 255, 0.5)');
|
|
982
|
+
horizontalLine.setAttribute('stroke-width', '1');
|
|
998
983
|
svg.appendChild(horizontalLine);
|
|
999
984
|
}
|
|
1000
985
|
overlay.appendChild(svg);
|
|
@@ -1007,12 +992,12 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
1007
992
|
}
|
|
1008
993
|
async getGridMode() {
|
|
1009
994
|
// Web implementation - default to none
|
|
1010
|
-
return { gridMode:
|
|
995
|
+
return { gridMode: 'none' };
|
|
1011
996
|
}
|
|
1012
997
|
async getPreviewSize() {
|
|
1013
998
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
1014
999
|
if (!video) {
|
|
1015
|
-
throw new Error(
|
|
1000
|
+
throw new Error('camera is not running');
|
|
1016
1001
|
}
|
|
1017
1002
|
const offsetX = video.width / 8;
|
|
1018
1003
|
const offsetY = video.height / 8;
|
|
@@ -1026,7 +1011,7 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
1026
1011
|
async setPreviewSize(options) {
|
|
1027
1012
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
1028
1013
|
if (!video) {
|
|
1029
|
-
throw new Error(
|
|
1014
|
+
throw new Error('camera is not running');
|
|
1030
1015
|
}
|
|
1031
1016
|
video.style.left = `${options.x}px`;
|
|
1032
1017
|
video.style.top = `${options.y}px`;
|
|
@@ -1044,16 +1029,16 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
1044
1029
|
async setFocus(options) {
|
|
1045
1030
|
// Reject if values are outside 0-1 range
|
|
1046
1031
|
if (options.x < 0 || options.x > 1 || options.y < 0 || options.y > 1) {
|
|
1047
|
-
throw new Error(
|
|
1032
|
+
throw new Error('Focus coordinates must be between 0 and 1');
|
|
1048
1033
|
}
|
|
1049
1034
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
1050
1035
|
if (!(video === null || video === void 0 ? void 0 : video.srcObject)) {
|
|
1051
|
-
throw new Error(
|
|
1036
|
+
throw new Error('camera is not running');
|
|
1052
1037
|
}
|
|
1053
1038
|
const stream = video.srcObject;
|
|
1054
1039
|
const videoTrack = stream.getVideoTracks()[0];
|
|
1055
1040
|
if (!videoTrack) {
|
|
1056
|
-
throw new Error(
|
|
1041
|
+
throw new Error('no video track found');
|
|
1057
1042
|
}
|
|
1058
1043
|
const capabilities = videoTrack.getCapabilities();
|
|
1059
1044
|
// Check if focusing is supported
|
|
@@ -1064,7 +1049,7 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
1064
1049
|
await videoTrack.applyConstraints({
|
|
1065
1050
|
advanced: [
|
|
1066
1051
|
{
|
|
1067
|
-
focusMode:
|
|
1052
|
+
focusMode: 'manual',
|
|
1068
1053
|
focusDistance: 0.5, // Mid-range focus as fallback
|
|
1069
1054
|
},
|
|
1070
1055
|
],
|
|
@@ -1075,30 +1060,30 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
1075
1060
|
}
|
|
1076
1061
|
}
|
|
1077
1062
|
else {
|
|
1078
|
-
console.warn(
|
|
1063
|
+
console.warn('Focus control is not supported on this device. Focus coordinates were provided but cannot be applied.');
|
|
1079
1064
|
}
|
|
1080
1065
|
}
|
|
1081
1066
|
// Exposure stubs (unsupported on web)
|
|
1082
1067
|
async getExposureModes() {
|
|
1083
|
-
throw new Error(
|
|
1068
|
+
throw new Error('getExposureModes not supported under the web platform');
|
|
1084
1069
|
}
|
|
1085
1070
|
async getExposureMode() {
|
|
1086
|
-
throw new Error(
|
|
1071
|
+
throw new Error('getExposureMode not supported under the web platform');
|
|
1087
1072
|
}
|
|
1088
1073
|
async setExposureMode(_options) {
|
|
1089
|
-
throw new Error(
|
|
1074
|
+
throw new Error('setExposureMode not supported under the web platform');
|
|
1090
1075
|
}
|
|
1091
1076
|
async getExposureCompensationRange() {
|
|
1092
|
-
throw new Error(
|
|
1077
|
+
throw new Error('getExposureCompensationRange not supported under the web platform');
|
|
1093
1078
|
}
|
|
1094
1079
|
async getExposureCompensation() {
|
|
1095
|
-
throw new Error(
|
|
1080
|
+
throw new Error('getExposureCompensation not supported under the web platform');
|
|
1096
1081
|
}
|
|
1097
1082
|
async setExposureCompensation(_options) {
|
|
1098
|
-
throw new Error(
|
|
1083
|
+
throw new Error('setExposureCompensation not supported under the web platform');
|
|
1099
1084
|
}
|
|
1100
1085
|
async deleteFile(_options) {
|
|
1101
|
-
throw new Error(
|
|
1086
|
+
throw new Error('deleteFile not supported under the web platform');
|
|
1102
1087
|
}
|
|
1103
1088
|
}
|
|
1104
1089
|
|