@capgo/camera-preview 7.23.10 → 7.23.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +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 +1833 -2289
- package/android/src/main/java/app/capgo/capacitor/camera/preview/CameraXView.java +3146 -3946
- 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/esm/web.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { WebPlugin } from
|
|
2
|
-
import { DeviceType } from
|
|
3
|
-
const DEFAULT_VIDEO_ID =
|
|
1
|
+
import { WebPlugin } from '@capacitor/core';
|
|
2
|
+
import { DeviceType } from './definitions';
|
|
3
|
+
const DEFAULT_VIDEO_ID = 'capgo_video';
|
|
4
4
|
export class CameraPreviewWeb extends WebPlugin {
|
|
5
5
|
constructor() {
|
|
6
6
|
super();
|
|
@@ -16,32 +16,32 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
16
16
|
}
|
|
17
17
|
async checkPermissions(options) {
|
|
18
18
|
const result = {
|
|
19
|
-
camera:
|
|
19
|
+
camera: 'prompt',
|
|
20
20
|
};
|
|
21
21
|
const permissionsApi = navigator === null || navigator === void 0 ? void 0 : navigator.permissions;
|
|
22
22
|
if (permissionsApi === null || permissionsApi === void 0 ? void 0 : permissionsApi.query) {
|
|
23
23
|
try {
|
|
24
|
-
const cameraPermission = await permissionsApi.query({ name:
|
|
24
|
+
const cameraPermission = await permissionsApi.query({ name: 'camera' });
|
|
25
25
|
result.camera = this.mapWebPermission(cameraPermission.state);
|
|
26
26
|
}
|
|
27
27
|
catch (error) {
|
|
28
|
-
console.warn(
|
|
28
|
+
console.warn('Camera permission query failed', error);
|
|
29
29
|
}
|
|
30
30
|
if ((options === null || options === void 0 ? void 0 : options.disableAudio) === false) {
|
|
31
31
|
try {
|
|
32
32
|
const microphonePermission = await permissionsApi.query({
|
|
33
|
-
name:
|
|
33
|
+
name: 'microphone',
|
|
34
34
|
});
|
|
35
35
|
result.microphone = this.mapWebPermission(microphonePermission.state);
|
|
36
36
|
}
|
|
37
37
|
catch (error) {
|
|
38
|
-
console.warn(
|
|
39
|
-
result.microphone =
|
|
38
|
+
console.warn('Microphone permission query failed', error);
|
|
39
|
+
result.microphone = 'prompt';
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
else if ((options === null || options === void 0 ? void 0 : options.disableAudio) === false) {
|
|
44
|
-
result.microphone =
|
|
44
|
+
result.microphone = 'prompt';
|
|
45
45
|
}
|
|
46
46
|
return result;
|
|
47
47
|
}
|
|
@@ -49,15 +49,13 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
49
49
|
var _a, _b;
|
|
50
50
|
const disableAudio = (_a = options === null || options === void 0 ? void 0 : options.disableAudio) !== null && _a !== void 0 ? _a : true;
|
|
51
51
|
if ((_b = navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices) === null || _b === void 0 ? void 0 : _b.getUserMedia) {
|
|
52
|
-
const constraints = disableAudio
|
|
53
|
-
? { video: true }
|
|
54
|
-
: { video: true, audio: true };
|
|
52
|
+
const constraints = disableAudio ? { video: true } : { video: true, audio: true };
|
|
55
53
|
let stream;
|
|
56
54
|
try {
|
|
57
55
|
stream = await navigator.mediaDevices.getUserMedia(constraints);
|
|
58
56
|
}
|
|
59
57
|
catch (error) {
|
|
60
|
-
console.warn(
|
|
58
|
+
console.warn('Unable to obtain camera or microphone stream', error);
|
|
61
59
|
}
|
|
62
60
|
finally {
|
|
63
61
|
try {
|
|
@@ -70,19 +68,19 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
70
68
|
}
|
|
71
69
|
const status = await this.checkPermissions({ disableAudio: disableAudio });
|
|
72
70
|
if (options === null || options === void 0 ? void 0 : options.showSettingsAlert) {
|
|
73
|
-
console.warn(
|
|
71
|
+
console.warn('showSettingsAlert is not supported on the web platform; returning permission status only.');
|
|
74
72
|
}
|
|
75
73
|
return status;
|
|
76
74
|
}
|
|
77
75
|
mapWebPermission(state) {
|
|
78
76
|
switch (state) {
|
|
79
|
-
case
|
|
80
|
-
return
|
|
81
|
-
case
|
|
82
|
-
return
|
|
83
|
-
case
|
|
77
|
+
case 'granted':
|
|
78
|
+
return 'granted';
|
|
79
|
+
case 'denied':
|
|
80
|
+
return 'denied';
|
|
81
|
+
case 'prompt':
|
|
84
82
|
default:
|
|
85
|
-
return
|
|
83
|
+
return 'prompt';
|
|
86
84
|
}
|
|
87
85
|
}
|
|
88
86
|
getCurrentOrientation() {
|
|
@@ -90,113 +88,111 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
90
88
|
try {
|
|
91
89
|
const so = screen.orientation;
|
|
92
90
|
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);
|
|
93
|
-
if (typeof type ===
|
|
94
|
-
if (type.includes(
|
|
95
|
-
return
|
|
96
|
-
if (type.includes(
|
|
97
|
-
return
|
|
98
|
-
if (type.includes(
|
|
99
|
-
return
|
|
100
|
-
if (type.includes(
|
|
101
|
-
return
|
|
102
|
-
if (type.includes(
|
|
103
|
-
return
|
|
104
|
-
if (type.includes(
|
|
105
|
-
return
|
|
91
|
+
if (typeof type === 'string') {
|
|
92
|
+
if (type.includes('portrait-primary'))
|
|
93
|
+
return 'portrait';
|
|
94
|
+
if (type.includes('portrait-secondary'))
|
|
95
|
+
return 'portrait-upside-down';
|
|
96
|
+
if (type.includes('landscape-primary'))
|
|
97
|
+
return 'landscape-left';
|
|
98
|
+
if (type.includes('landscape-secondary'))
|
|
99
|
+
return 'landscape-right';
|
|
100
|
+
if (type.includes('landscape'))
|
|
101
|
+
return 'landscape-right'; // avoid generic landscape
|
|
102
|
+
if (type.includes('portrait'))
|
|
103
|
+
return 'portrait';
|
|
106
104
|
}
|
|
107
105
|
const angle = window.orientation;
|
|
108
|
-
if (typeof angle ===
|
|
106
|
+
if (typeof angle === 'number') {
|
|
109
107
|
if (angle === 0)
|
|
110
|
-
return
|
|
108
|
+
return 'portrait';
|
|
111
109
|
if (angle === 180)
|
|
112
|
-
return
|
|
110
|
+
return 'portrait-upside-down';
|
|
113
111
|
if (angle === 90)
|
|
114
|
-
return
|
|
112
|
+
return 'landscape-right';
|
|
115
113
|
if (angle === -90)
|
|
116
|
-
return
|
|
114
|
+
return 'landscape-left';
|
|
117
115
|
if (angle === 270)
|
|
118
|
-
return
|
|
116
|
+
return 'landscape-left';
|
|
119
117
|
}
|
|
120
|
-
if ((_a = window.matchMedia(
|
|
121
|
-
return
|
|
118
|
+
if ((_a = window.matchMedia('(orientation: portrait)')) === null || _a === void 0 ? void 0 : _a.matches) {
|
|
119
|
+
return 'portrait';
|
|
122
120
|
}
|
|
123
|
-
if ((_b = window.matchMedia(
|
|
121
|
+
if ((_b = window.matchMedia('(orientation: landscape)')) === null || _b === void 0 ? void 0 : _b.matches) {
|
|
124
122
|
// Default to landscape-right when we can't distinguish primary/secondary
|
|
125
|
-
return
|
|
123
|
+
return 'landscape-right';
|
|
126
124
|
}
|
|
127
125
|
}
|
|
128
126
|
catch (e) {
|
|
129
127
|
console.error(e);
|
|
130
128
|
}
|
|
131
|
-
return
|
|
129
|
+
return 'unknown';
|
|
132
130
|
}
|
|
133
131
|
ensureOrientationListener() {
|
|
134
132
|
if (this.orientationListenerBound)
|
|
135
133
|
return;
|
|
136
134
|
const emit = () => {
|
|
137
|
-
this.notifyListeners(
|
|
135
|
+
this.notifyListeners('orientationChange', {
|
|
138
136
|
orientation: this.getCurrentOrientation(),
|
|
139
137
|
});
|
|
140
138
|
};
|
|
141
|
-
window.addEventListener(
|
|
142
|
-
window.addEventListener(
|
|
139
|
+
window.addEventListener('orientationchange', emit);
|
|
140
|
+
window.addEventListener('resize', emit);
|
|
143
141
|
this.orientationListenerBound = true;
|
|
144
142
|
}
|
|
145
143
|
async getOrientation() {
|
|
146
144
|
return { orientation: this.getCurrentOrientation() };
|
|
147
145
|
}
|
|
148
146
|
getSafeAreaInsets() {
|
|
149
|
-
throw new Error(
|
|
147
|
+
throw new Error('Method not implemented.');
|
|
150
148
|
}
|
|
151
149
|
async getZoomButtonValues() {
|
|
152
|
-
throw new Error(
|
|
150
|
+
throw new Error('getZoomButtonValues not supported under the web platform');
|
|
153
151
|
}
|
|
154
152
|
async getSupportedPictureSizes() {
|
|
155
|
-
throw new Error(
|
|
153
|
+
throw new Error('getSupportedPictureSizes not supported under the web platform');
|
|
156
154
|
}
|
|
157
155
|
async start(options) {
|
|
158
156
|
if (options.aspectRatio && (options.width || options.height)) {
|
|
159
|
-
throw new Error(
|
|
157
|
+
throw new Error('Cannot set both aspectRatio and size (width/height). Use setPreviewSize after start.');
|
|
160
158
|
}
|
|
161
159
|
if (this.isStarted) {
|
|
162
|
-
throw new Error(
|
|
160
|
+
throw new Error('camera already started');
|
|
163
161
|
}
|
|
164
162
|
this.isBackCamera = true;
|
|
165
163
|
this.isStarted = false;
|
|
166
|
-
const parent = document.getElementById((options === null || options === void 0 ? void 0 : options.parent) ||
|
|
167
|
-
const gridMode = (options === null || options === void 0 ? void 0 : options.gridMode) ||
|
|
168
|
-
const positioning = (options === null || options === void 0 ? void 0 : options.positioning) ||
|
|
164
|
+
const parent = document.getElementById((options === null || options === void 0 ? void 0 : options.parent) || '');
|
|
165
|
+
const gridMode = (options === null || options === void 0 ? void 0 : options.gridMode) || 'none';
|
|
166
|
+
const positioning = (options === null || options === void 0 ? void 0 : options.positioning) || 'top';
|
|
169
167
|
if (options.position) {
|
|
170
|
-
this.isBackCamera = options.position ===
|
|
168
|
+
this.isBackCamera = options.position === 'rear';
|
|
171
169
|
}
|
|
172
170
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
173
171
|
if (video) {
|
|
174
172
|
video.remove();
|
|
175
173
|
}
|
|
176
|
-
const container = options.parent
|
|
177
|
-
? document.getElementById(options.parent)
|
|
178
|
-
: document.body;
|
|
174
|
+
const container = options.parent ? document.getElementById(options.parent) : document.body;
|
|
179
175
|
if (!container) {
|
|
180
|
-
throw new Error(
|
|
176
|
+
throw new Error('container not found');
|
|
181
177
|
}
|
|
182
|
-
this.videoElement = document.createElement(
|
|
178
|
+
this.videoElement = document.createElement('video');
|
|
183
179
|
this.videoElement.id = DEFAULT_VIDEO_ID;
|
|
184
|
-
this.videoElement.className = options.className ||
|
|
180
|
+
this.videoElement.className = options.className || '';
|
|
185
181
|
this.videoElement.playsInline = true;
|
|
186
182
|
this.videoElement.muted = true;
|
|
187
183
|
this.videoElement.autoplay = true;
|
|
188
184
|
// Remove objectFit as we'll match camera's native aspect ratio
|
|
189
|
-
this.videoElement.style.backgroundColor =
|
|
185
|
+
this.videoElement.style.backgroundColor = 'transparent';
|
|
190
186
|
// Reset any default margins that might interfere
|
|
191
|
-
this.videoElement.style.margin =
|
|
192
|
-
this.videoElement.style.padding =
|
|
187
|
+
this.videoElement.style.margin = '0';
|
|
188
|
+
this.videoElement.style.padding = '0';
|
|
193
189
|
container.appendChild(this.videoElement);
|
|
194
190
|
if (options.toBack) {
|
|
195
|
-
this.videoElement.style.zIndex =
|
|
191
|
+
this.videoElement.style.zIndex = '-1';
|
|
196
192
|
}
|
|
197
193
|
// Default to 4:3 if no aspect ratio or size specified
|
|
198
194
|
const useDefaultAspectRatio = !options.aspectRatio && !options.width && !options.height;
|
|
199
|
-
const effectiveAspectRatio = options.aspectRatio || (useDefaultAspectRatio ?
|
|
195
|
+
const effectiveAspectRatio = options.aspectRatio || (useDefaultAspectRatio ? '4:3' : null);
|
|
200
196
|
if (options.width) {
|
|
201
197
|
this.videoElement.width = options.width;
|
|
202
198
|
this.videoElement.style.width = `${options.width}px`;
|
|
@@ -209,8 +205,8 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
209
205
|
const centerX = options.x === undefined;
|
|
210
206
|
const centerY = options.y === undefined;
|
|
211
207
|
// Always set position to absolute for proper positioning
|
|
212
|
-
this.videoElement.style.position =
|
|
213
|
-
console.log(
|
|
208
|
+
this.videoElement.style.position = 'absolute';
|
|
209
|
+
console.log('Initial positioning flags:', {
|
|
214
210
|
centerX,
|
|
215
211
|
centerY,
|
|
216
212
|
x: options.x,
|
|
@@ -223,28 +219,28 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
223
219
|
this.videoElement.style.top = `${options.y}px`;
|
|
224
220
|
}
|
|
225
221
|
// Create and add grid overlay if needed
|
|
226
|
-
if (gridMode !==
|
|
222
|
+
if (gridMode !== 'none') {
|
|
227
223
|
const gridOverlay = this.createGridOverlay(gridMode);
|
|
228
|
-
gridOverlay.id =
|
|
224
|
+
gridOverlay.id = 'camera-grid-overlay';
|
|
229
225
|
parent === null || parent === void 0 ? void 0 : parent.appendChild(gridOverlay);
|
|
230
226
|
}
|
|
231
227
|
// Aspect ratio handling is now done after getting camera stream
|
|
232
228
|
// Store centering flags for later use
|
|
233
229
|
const needsCenterX = centerX;
|
|
234
230
|
const needsCenterY = centerY;
|
|
235
|
-
console.log(
|
|
231
|
+
console.log('Centering flags stored:', { needsCenterX, needsCenterY });
|
|
236
232
|
// First get the camera stream with basic constraints
|
|
237
233
|
const constraints = {
|
|
238
234
|
video: {
|
|
239
|
-
facingMode: this.isBackCamera ?
|
|
235
|
+
facingMode: this.isBackCamera ? 'environment' : 'user',
|
|
240
236
|
},
|
|
241
237
|
};
|
|
242
238
|
const stream = await navigator.mediaDevices.getUserMedia(constraints);
|
|
243
239
|
if (!stream) {
|
|
244
|
-
throw new Error(
|
|
240
|
+
throw new Error('could not acquire stream');
|
|
245
241
|
}
|
|
246
242
|
if (!this.videoElement) {
|
|
247
|
-
throw new Error(
|
|
243
|
+
throw new Error('video element not found');
|
|
248
244
|
}
|
|
249
245
|
// Get the actual camera dimensions from the video track
|
|
250
246
|
const videoTrack = stream.getVideoTracks()[0];
|
|
@@ -252,12 +248,12 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
252
248
|
const cameraWidth = settings.width || 640;
|
|
253
249
|
const cameraHeight = settings.height || 480;
|
|
254
250
|
const cameraAspectRatio = cameraWidth / cameraHeight;
|
|
255
|
-
console.log(
|
|
251
|
+
console.log('Camera native dimensions:', {
|
|
256
252
|
width: cameraWidth,
|
|
257
253
|
height: cameraHeight,
|
|
258
254
|
aspectRatio: cameraAspectRatio,
|
|
259
255
|
});
|
|
260
|
-
console.log(
|
|
256
|
+
console.log('Container dimensions:', {
|
|
261
257
|
width: container.offsetWidth,
|
|
262
258
|
height: container.offsetHeight,
|
|
263
259
|
id: container.id,
|
|
@@ -277,7 +273,7 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
277
273
|
targetHeight = containerHeight;
|
|
278
274
|
targetWidth = targetHeight * cameraAspectRatio;
|
|
279
275
|
}
|
|
280
|
-
console.log(
|
|
276
|
+
console.log('Video element dimensions:', {
|
|
281
277
|
width: targetWidth,
|
|
282
278
|
height: targetHeight,
|
|
283
279
|
container: { width: containerWidth, height: containerHeight },
|
|
@@ -294,21 +290,21 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
294
290
|
if (needsCenterY || options.y === undefined) {
|
|
295
291
|
let y;
|
|
296
292
|
switch (positioning) {
|
|
297
|
-
case
|
|
293
|
+
case 'top':
|
|
298
294
|
y = 0;
|
|
299
295
|
break;
|
|
300
|
-
case
|
|
296
|
+
case 'bottom':
|
|
301
297
|
y = window.innerHeight - targetHeight;
|
|
302
298
|
break;
|
|
303
|
-
case
|
|
299
|
+
case 'center':
|
|
304
300
|
default:
|
|
305
301
|
y = Math.round((window.innerHeight - targetHeight) / 2);
|
|
306
302
|
break;
|
|
307
303
|
}
|
|
308
|
-
this.videoElement.style.setProperty(
|
|
304
|
+
this.videoElement.style.setProperty('top', `${y}px`, 'important');
|
|
309
305
|
// Force a style recalculation
|
|
310
306
|
this.videoElement.offsetHeight;
|
|
311
|
-
console.log(
|
|
307
|
+
console.log('Positioning video:', {
|
|
312
308
|
positioning,
|
|
313
309
|
viewportHeight: window.innerHeight,
|
|
314
310
|
targetHeight,
|
|
@@ -320,9 +316,7 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
320
316
|
}
|
|
321
317
|
else if (effectiveAspectRatio && !options.width && !options.height) {
|
|
322
318
|
// Aspect ratio specified but no size
|
|
323
|
-
const [widthRatio, heightRatio] = effectiveAspectRatio
|
|
324
|
-
.split(":")
|
|
325
|
-
.map(Number);
|
|
319
|
+
const [widthRatio, heightRatio] = effectiveAspectRatio.split(':').map(Number);
|
|
326
320
|
const targetRatio = widthRatio / heightRatio;
|
|
327
321
|
const viewportWidth = window.innerWidth;
|
|
328
322
|
const viewportHeight = window.innerHeight;
|
|
@@ -349,13 +343,13 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
349
343
|
const parentHeight = container.offsetHeight || viewportHeight;
|
|
350
344
|
let y;
|
|
351
345
|
switch (positioning) {
|
|
352
|
-
case
|
|
346
|
+
case 'top':
|
|
353
347
|
y = 0;
|
|
354
348
|
break;
|
|
355
|
-
case
|
|
349
|
+
case 'bottom':
|
|
356
350
|
y = parentHeight - targetHeight;
|
|
357
351
|
break;
|
|
358
|
-
case
|
|
352
|
+
case 'center':
|
|
359
353
|
default:
|
|
360
354
|
y = Math.round((parentHeight - targetHeight) / 2);
|
|
361
355
|
break;
|
|
@@ -365,11 +359,10 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
365
359
|
}
|
|
366
360
|
this.videoElement.srcObject = stream;
|
|
367
361
|
if (!this.isBackCamera) {
|
|
368
|
-
this.videoElement.style.transform =
|
|
362
|
+
this.videoElement.style.transform = 'scaleX(-1)';
|
|
369
363
|
}
|
|
370
364
|
// Set initial zoom level if specified and supported
|
|
371
|
-
if (options.initialZoomLevel !== undefined &&
|
|
372
|
-
options.initialZoomLevel !== 1.0) {
|
|
365
|
+
if (options.initialZoomLevel !== undefined && options.initialZoomLevel !== 1.0) {
|
|
373
366
|
// videoTrack already declared above
|
|
374
367
|
if (videoTrack) {
|
|
375
368
|
const capabilities = videoTrack.getCapabilities();
|
|
@@ -399,26 +392,26 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
399
392
|
await new Promise((resolve) => {
|
|
400
393
|
const videoEl = this.videoElement;
|
|
401
394
|
if (!videoEl) {
|
|
402
|
-
throw new Error(
|
|
395
|
+
throw new Error('video element not found');
|
|
403
396
|
}
|
|
404
397
|
if (videoEl.readyState >= 2) {
|
|
405
398
|
resolve();
|
|
406
399
|
}
|
|
407
400
|
else {
|
|
408
|
-
videoEl.addEventListener(
|
|
401
|
+
videoEl.addEventListener('loadeddata', () => resolve(), {
|
|
409
402
|
once: true,
|
|
410
403
|
});
|
|
411
404
|
}
|
|
412
405
|
});
|
|
413
406
|
// Ensure centering is applied after DOM updates
|
|
414
407
|
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
415
|
-
console.log(
|
|
408
|
+
console.log('About to re-center, flags:', { needsCenterX, needsCenterY });
|
|
416
409
|
// Re-apply centering with correct parent dimensions
|
|
417
410
|
if (needsCenterX) {
|
|
418
411
|
const parentWidth = container.offsetWidth;
|
|
419
412
|
const x = Math.round((parentWidth - this.videoElement.offsetWidth) / 2);
|
|
420
413
|
this.videoElement.style.left = `${x}px`;
|
|
421
|
-
console.log(
|
|
414
|
+
console.log('Re-centering X:', {
|
|
422
415
|
parentWidth,
|
|
423
416
|
videoWidth: this.videoElement.offsetWidth,
|
|
424
417
|
x,
|
|
@@ -427,19 +420,19 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
427
420
|
if (needsCenterY) {
|
|
428
421
|
let y;
|
|
429
422
|
switch (positioning) {
|
|
430
|
-
case
|
|
423
|
+
case 'top':
|
|
431
424
|
y = 0;
|
|
432
425
|
break;
|
|
433
|
-
case
|
|
426
|
+
case 'bottom':
|
|
434
427
|
y = window.innerHeight - this.videoElement.offsetHeight;
|
|
435
428
|
break;
|
|
436
|
-
case
|
|
429
|
+
case 'center':
|
|
437
430
|
default:
|
|
438
431
|
y = Math.round((window.innerHeight - this.videoElement.offsetHeight) / 2);
|
|
439
432
|
break;
|
|
440
433
|
}
|
|
441
|
-
this.videoElement.style.setProperty(
|
|
442
|
-
console.log(
|
|
434
|
+
this.videoElement.style.setProperty('top', `${y}px`, 'important');
|
|
435
|
+
console.log('Re-positioning Y:', {
|
|
443
436
|
positioning,
|
|
444
437
|
viewportHeight: window.innerHeight,
|
|
445
438
|
videoHeight: this.videoElement.offsetHeight,
|
|
@@ -451,7 +444,7 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
451
444
|
// Get the actual rendered dimensions after video is loaded
|
|
452
445
|
const rect = this.videoElement.getBoundingClientRect();
|
|
453
446
|
const computedStyle = window.getComputedStyle(this.videoElement);
|
|
454
|
-
console.log(
|
|
447
|
+
console.log('Final video element state:', {
|
|
455
448
|
rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
|
|
456
449
|
style: {
|
|
457
450
|
position: computedStyle.position,
|
|
@@ -484,21 +477,21 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
484
477
|
this.isStarted = false;
|
|
485
478
|
}
|
|
486
479
|
// Remove grid overlay if it exists
|
|
487
|
-
const gridOverlay = document.getElementById(
|
|
480
|
+
const gridOverlay = document.getElementById('camera-grid-overlay');
|
|
488
481
|
gridOverlay === null || gridOverlay === void 0 ? void 0 : gridOverlay.remove();
|
|
489
482
|
}
|
|
490
483
|
async capture(options) {
|
|
491
484
|
return new Promise((resolve, reject) => {
|
|
492
485
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
493
486
|
if (!(video === null || video === void 0 ? void 0 : video.srcObject)) {
|
|
494
|
-
reject(new Error(
|
|
487
|
+
reject(new Error('camera is not running'));
|
|
495
488
|
return;
|
|
496
489
|
}
|
|
497
490
|
// video.width = video.offsetWidth;
|
|
498
491
|
let base64EncodedImage;
|
|
499
492
|
if (video && video.videoWidth > 0 && video.videoHeight > 0) {
|
|
500
|
-
const canvas = document.createElement(
|
|
501
|
-
const context = canvas.getContext(
|
|
493
|
+
const canvas = document.createElement('canvas');
|
|
494
|
+
const context = canvas.getContext('2d');
|
|
502
495
|
// Calculate capture dimensions
|
|
503
496
|
let captureWidth = video.videoWidth;
|
|
504
497
|
let captureHeight = video.videoHeight;
|
|
@@ -549,15 +542,13 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
549
542
|
if (options.withExifLocation) {
|
|
550
543
|
// withExifLocation is not supported on web
|
|
551
544
|
}
|
|
552
|
-
if ((options.format ||
|
|
545
|
+
if ((options.format || 'jpeg') === 'jpeg') {
|
|
553
546
|
base64EncodedImage = canvas
|
|
554
|
-
.toDataURL(
|
|
555
|
-
.replace(
|
|
547
|
+
.toDataURL('image/jpeg', (options.quality || 85) / 100.0)
|
|
548
|
+
.replace('data:image/jpeg;base64,', '');
|
|
556
549
|
}
|
|
557
550
|
else {
|
|
558
|
-
base64EncodedImage = canvas
|
|
559
|
-
.toDataURL("image/png")
|
|
560
|
-
.replace("data:image/png;base64,", "");
|
|
551
|
+
base64EncodedImage = canvas.toDataURL('image/png').replace('data:image/png;base64,', '');
|
|
561
552
|
}
|
|
562
553
|
}
|
|
563
554
|
resolve({
|
|
@@ -570,17 +561,17 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
570
561
|
return this.capture(_options);
|
|
571
562
|
}
|
|
572
563
|
async stopRecordVideo() {
|
|
573
|
-
throw new Error(
|
|
564
|
+
throw new Error('stopRecordVideo not supported under the web platform');
|
|
574
565
|
}
|
|
575
566
|
async startRecordVideo(_options) {
|
|
576
|
-
console.log(
|
|
577
|
-
throw new Error(
|
|
567
|
+
console.log('startRecordVideo', _options);
|
|
568
|
+
throw new Error('startRecordVideo not supported under the web platform');
|
|
578
569
|
}
|
|
579
570
|
async getSupportedFlashModes() {
|
|
580
|
-
throw new Error(
|
|
571
|
+
throw new Error('getSupportedFlashModes not supported under the web platform');
|
|
581
572
|
}
|
|
582
573
|
async getHorizontalFov() {
|
|
583
|
-
throw new Error(
|
|
574
|
+
throw new Error('getHorizontalFov not supported under the web platform');
|
|
584
575
|
}
|
|
585
576
|
async setFlashMode(_options) {
|
|
586
577
|
throw new Error(`setFlashMode not supported under the web platform${_options}`);
|
|
@@ -588,7 +579,7 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
588
579
|
async flip() {
|
|
589
580
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
590
581
|
if (!(video === null || video === void 0 ? void 0 : video.srcObject)) {
|
|
591
|
-
throw new Error(
|
|
582
|
+
throw new Error('camera is not running');
|
|
592
583
|
}
|
|
593
584
|
// Stop current stream
|
|
594
585
|
this.stopStream(video.srcObject);
|
|
@@ -597,7 +588,7 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
597
588
|
// Get new constraints
|
|
598
589
|
const constraints = {
|
|
599
590
|
video: {
|
|
600
|
-
facingMode: this.isBackCamera ?
|
|
591
|
+
facingMode: this.isBackCamera ? 'environment' : 'user',
|
|
601
592
|
width: { ideal: video.videoWidth || 640 },
|
|
602
593
|
height: { ideal: video.videoHeight || 480 },
|
|
603
594
|
},
|
|
@@ -612,12 +603,12 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
612
603
|
}
|
|
613
604
|
// Update video transform based on camera
|
|
614
605
|
if (this.isBackCamera) {
|
|
615
|
-
video.style.transform =
|
|
616
|
-
video.style.webkitTransform =
|
|
606
|
+
video.style.transform = 'none';
|
|
607
|
+
video.style.webkitTransform = 'none';
|
|
617
608
|
}
|
|
618
609
|
else {
|
|
619
|
-
video.style.transform =
|
|
620
|
-
video.style.webkitTransform =
|
|
610
|
+
video.style.transform = 'scaleX(-1)';
|
|
611
|
+
video.style.webkitTransform = 'scaleX(-1)';
|
|
621
612
|
}
|
|
622
613
|
await video.play();
|
|
623
614
|
}
|
|
@@ -628,7 +619,7 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
628
619
|
async setOpacity(_options) {
|
|
629
620
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
630
621
|
if (!!video && !!_options.opacity)
|
|
631
|
-
video.style.setProperty(
|
|
622
|
+
video.style.setProperty('opacity', _options.opacity.toString());
|
|
632
623
|
}
|
|
633
624
|
async isRunning() {
|
|
634
625
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
@@ -637,10 +628,10 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
637
628
|
async getAvailableDevices() {
|
|
638
629
|
var _a;
|
|
639
630
|
if (!((_a = navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.enumerateDevices)) {
|
|
640
|
-
throw new Error(
|
|
631
|
+
throw new Error('getAvailableDevices not supported under the web platform');
|
|
641
632
|
}
|
|
642
633
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
643
|
-
const videoDevices = devices.filter((device) => device.kind ===
|
|
634
|
+
const videoDevices = devices.filter((device) => device.kind === 'videoinput');
|
|
644
635
|
// Group devices by position (front/back)
|
|
645
636
|
const frontDevices = [];
|
|
646
637
|
const backDevices = [];
|
|
@@ -650,19 +641,18 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
650
641
|
// Determine device type based on label
|
|
651
642
|
let deviceType = DeviceType.WIDE_ANGLE;
|
|
652
643
|
let baseZoomRatio = 1.0;
|
|
653
|
-
if (labelLower.includes(
|
|
644
|
+
if (labelLower.includes('ultra') || labelLower.includes('0.5')) {
|
|
654
645
|
deviceType = DeviceType.ULTRA_WIDE;
|
|
655
646
|
baseZoomRatio = 0.5;
|
|
656
647
|
}
|
|
657
|
-
else if (labelLower.includes(
|
|
658
|
-
labelLower.includes(
|
|
659
|
-
labelLower.includes(
|
|
660
|
-
labelLower.includes(
|
|
648
|
+
else if (labelLower.includes('telephoto') ||
|
|
649
|
+
labelLower.includes('tele') ||
|
|
650
|
+
labelLower.includes('2x') ||
|
|
651
|
+
labelLower.includes('3x')) {
|
|
661
652
|
deviceType = DeviceType.TELEPHOTO;
|
|
662
653
|
baseZoomRatio = 2.0;
|
|
663
654
|
}
|
|
664
|
-
else if (labelLower.includes(
|
|
665
|
-
labelLower.includes("truedepth")) {
|
|
655
|
+
else if (labelLower.includes('depth') || labelLower.includes('truedepth')) {
|
|
666
656
|
deviceType = DeviceType.TRUE_DEPTH;
|
|
667
657
|
baseZoomRatio = 1.0;
|
|
668
658
|
}
|
|
@@ -676,7 +666,7 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
676
666
|
maxZoom: 1.0,
|
|
677
667
|
};
|
|
678
668
|
// Determine position and add to appropriate array
|
|
679
|
-
if (labelLower.includes(
|
|
669
|
+
if (labelLower.includes('back') || labelLower.includes('rear')) {
|
|
680
670
|
backDevices.push(lensInfo);
|
|
681
671
|
}
|
|
682
672
|
else {
|
|
@@ -687,8 +677,8 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
687
677
|
if (frontDevices.length > 0) {
|
|
688
678
|
result.push({
|
|
689
679
|
deviceId: frontDevices[0].deviceId,
|
|
690
|
-
label:
|
|
691
|
-
position:
|
|
680
|
+
label: 'Front Camera',
|
|
681
|
+
position: 'front',
|
|
692
682
|
lenses: frontDevices,
|
|
693
683
|
isLogical: false,
|
|
694
684
|
minZoom: Math.min(...frontDevices.map((d) => d.minZoom)),
|
|
@@ -698,8 +688,8 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
698
688
|
if (backDevices.length > 0) {
|
|
699
689
|
result.push({
|
|
700
690
|
deviceId: backDevices[0].deviceId,
|
|
701
|
-
label:
|
|
702
|
-
position:
|
|
691
|
+
label: 'Back Camera',
|
|
692
|
+
position: 'rear',
|
|
703
693
|
lenses: backDevices,
|
|
704
694
|
isLogical: false,
|
|
705
695
|
minZoom: Math.min(...backDevices.map((d) => d.minZoom)),
|
|
@@ -711,17 +701,17 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
711
701
|
async getZoom() {
|
|
712
702
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
713
703
|
if (!(video === null || video === void 0 ? void 0 : video.srcObject)) {
|
|
714
|
-
throw new Error(
|
|
704
|
+
throw new Error('camera is not running');
|
|
715
705
|
}
|
|
716
706
|
const stream = video.srcObject;
|
|
717
707
|
const videoTrack = stream.getVideoTracks()[0];
|
|
718
708
|
if (!videoTrack) {
|
|
719
|
-
throw new Error(
|
|
709
|
+
throw new Error('no video track found');
|
|
720
710
|
}
|
|
721
711
|
const capabilities = videoTrack.getCapabilities();
|
|
722
712
|
const settings = videoTrack.getSettings();
|
|
723
713
|
if (!capabilities.zoom) {
|
|
724
|
-
throw new Error(
|
|
714
|
+
throw new Error('zoom not supported by this device');
|
|
725
715
|
}
|
|
726
716
|
// Get current device info to determine lens type
|
|
727
717
|
let deviceType = DeviceType.WIDE_ANGLE;
|
|
@@ -731,19 +721,18 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
731
721
|
const device = devices.find((d) => d.deviceId === this.currentDeviceId);
|
|
732
722
|
if (device) {
|
|
733
723
|
const labelLower = device.label.toLowerCase();
|
|
734
|
-
if (labelLower.includes(
|
|
724
|
+
if (labelLower.includes('ultra') || labelLower.includes('0.5')) {
|
|
735
725
|
deviceType = DeviceType.ULTRA_WIDE;
|
|
736
726
|
baseZoomRatio = 0.5;
|
|
737
727
|
}
|
|
738
|
-
else if (labelLower.includes(
|
|
739
|
-
labelLower.includes(
|
|
740
|
-
labelLower.includes(
|
|
741
|
-
labelLower.includes(
|
|
728
|
+
else if (labelLower.includes('telephoto') ||
|
|
729
|
+
labelLower.includes('tele') ||
|
|
730
|
+
labelLower.includes('2x') ||
|
|
731
|
+
labelLower.includes('3x')) {
|
|
742
732
|
deviceType = DeviceType.TELEPHOTO;
|
|
743
733
|
baseZoomRatio = 2.0;
|
|
744
734
|
}
|
|
745
|
-
else if (labelLower.includes(
|
|
746
|
-
labelLower.includes("truedepth")) {
|
|
735
|
+
else if (labelLower.includes('depth') || labelLower.includes('truedepth')) {
|
|
747
736
|
deviceType = DeviceType.TRUE_DEPTH;
|
|
748
737
|
baseZoomRatio = 1.0;
|
|
749
738
|
}
|
|
@@ -766,16 +755,16 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
766
755
|
async setZoom(options) {
|
|
767
756
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
768
757
|
if (!(video === null || video === void 0 ? void 0 : video.srcObject)) {
|
|
769
|
-
throw new Error(
|
|
758
|
+
throw new Error('camera is not running');
|
|
770
759
|
}
|
|
771
760
|
const stream = video.srcObject;
|
|
772
761
|
const videoTrack = stream.getVideoTracks()[0];
|
|
773
762
|
if (!videoTrack) {
|
|
774
|
-
throw new Error(
|
|
763
|
+
throw new Error('no video track found');
|
|
775
764
|
}
|
|
776
765
|
const capabilities = videoTrack.getCapabilities();
|
|
777
766
|
if (!capabilities.zoom) {
|
|
778
|
-
throw new Error(
|
|
767
|
+
throw new Error('zoom not supported by this device');
|
|
779
768
|
}
|
|
780
769
|
const zoomLevel = Math.max(capabilities.zoom.min || 1, Math.min(capabilities.zoom.max || 1, options.level));
|
|
781
770
|
// Note: autoFocus is not supported on web platform
|
|
@@ -789,15 +778,15 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
789
778
|
}
|
|
790
779
|
}
|
|
791
780
|
async getFlashMode() {
|
|
792
|
-
throw new Error(
|
|
781
|
+
throw new Error('getFlashMode not supported under the web platform');
|
|
793
782
|
}
|
|
794
783
|
async getDeviceId() {
|
|
795
|
-
return { deviceId: this.currentDeviceId ||
|
|
784
|
+
return { deviceId: this.currentDeviceId || '' };
|
|
796
785
|
}
|
|
797
786
|
async setDeviceId(options) {
|
|
798
787
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
799
788
|
if (!(video === null || video === void 0 ? void 0 : video.srcObject)) {
|
|
800
|
-
throw new Error(
|
|
789
|
+
throw new Error('camera is not running');
|
|
801
790
|
}
|
|
802
791
|
// Stop current stream
|
|
803
792
|
this.stopStream(video.srcObject);
|
|
@@ -816,19 +805,17 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
816
805
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
817
806
|
const device = devices.find((d) => d.deviceId === options.deviceId);
|
|
818
807
|
this.isBackCamera =
|
|
819
|
-
(device === null || device === void 0 ? void 0 : device.label.toLowerCase().includes(
|
|
820
|
-
(device === null || device === void 0 ? void 0 : device.label.toLowerCase().includes("rear")) ||
|
|
821
|
-
false;
|
|
808
|
+
(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;
|
|
822
809
|
const stream = await navigator.mediaDevices.getUserMedia(constraints);
|
|
823
810
|
video.srcObject = stream;
|
|
824
811
|
// Update video transform based on camera
|
|
825
812
|
if (this.isBackCamera) {
|
|
826
|
-
video.style.transform =
|
|
827
|
-
video.style.webkitTransform =
|
|
813
|
+
video.style.transform = 'none';
|
|
814
|
+
video.style.webkitTransform = 'none';
|
|
828
815
|
}
|
|
829
816
|
else {
|
|
830
|
-
video.style.transform =
|
|
831
|
-
video.style.webkitTransform =
|
|
817
|
+
video.style.transform = 'scaleX(-1)';
|
|
818
|
+
video.style.webkitTransform = 'scaleX(-1)';
|
|
832
819
|
}
|
|
833
820
|
await video.play();
|
|
834
821
|
}
|
|
@@ -839,7 +826,7 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
839
826
|
async getAspectRatio() {
|
|
840
827
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
841
828
|
if (!video) {
|
|
842
|
-
throw new Error(
|
|
829
|
+
throw new Error('camera is not running');
|
|
843
830
|
}
|
|
844
831
|
const width = video.offsetWidth;
|
|
845
832
|
const height = video.offsetHeight;
|
|
@@ -847,24 +834,22 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
847
834
|
const ratio = width / height;
|
|
848
835
|
// Check for portrait camera ratios: 4:3 -> 3:4, 16:9 -> 9:16
|
|
849
836
|
if (Math.abs(ratio - 3 / 4) < 0.01) {
|
|
850
|
-
return { aspectRatio:
|
|
837
|
+
return { aspectRatio: '4:3' };
|
|
851
838
|
}
|
|
852
839
|
if (Math.abs(ratio - 9 / 16) < 0.01) {
|
|
853
|
-
return { aspectRatio:
|
|
840
|
+
return { aspectRatio: '16:9' };
|
|
854
841
|
}
|
|
855
842
|
}
|
|
856
843
|
// Default to 4:3 if no specific aspect ratio is matched
|
|
857
|
-
return { aspectRatio:
|
|
844
|
+
return { aspectRatio: '4:3' };
|
|
858
845
|
}
|
|
859
846
|
async setAspectRatio(options) {
|
|
860
847
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
861
848
|
if (!video) {
|
|
862
|
-
throw new Error(
|
|
849
|
+
throw new Error('camera is not running');
|
|
863
850
|
}
|
|
864
851
|
if (options.aspectRatio) {
|
|
865
|
-
const [widthRatio, heightRatio] = options.aspectRatio
|
|
866
|
-
.split(":")
|
|
867
|
-
.map(Number);
|
|
852
|
+
const [widthRatio, heightRatio] = options.aspectRatio.split(':').map(Number);
|
|
868
853
|
// For camera, use portrait orientation: 4:3 becomes 3:4, 16:9 becomes 9:16
|
|
869
854
|
const ratio = heightRatio / widthRatio;
|
|
870
855
|
// Get current position and size
|
|
@@ -900,7 +885,7 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
900
885
|
video.style.height = `${newHeight}px`;
|
|
901
886
|
video.style.left = `${x}px`;
|
|
902
887
|
video.style.top = `${y}px`;
|
|
903
|
-
video.style.position =
|
|
888
|
+
video.style.position = 'absolute';
|
|
904
889
|
const offsetX = newWidth / 8;
|
|
905
890
|
const offsetY = newHeight / 8;
|
|
906
891
|
return {
|
|
@@ -911,7 +896,7 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
911
896
|
};
|
|
912
897
|
}
|
|
913
898
|
else {
|
|
914
|
-
video.style.objectFit =
|
|
899
|
+
video.style.objectFit = 'cover';
|
|
915
900
|
const rect = video.getBoundingClientRect();
|
|
916
901
|
const offsetX = rect.width / 8;
|
|
917
902
|
const offsetY = rect.height / 8;
|
|
@@ -924,41 +909,41 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
924
909
|
}
|
|
925
910
|
}
|
|
926
911
|
createGridOverlay(gridMode) {
|
|
927
|
-
const overlay = document.createElement(
|
|
928
|
-
overlay.style.position =
|
|
929
|
-
overlay.style.top =
|
|
930
|
-
overlay.style.left =
|
|
931
|
-
overlay.style.width =
|
|
932
|
-
overlay.style.height =
|
|
933
|
-
overlay.style.pointerEvents =
|
|
934
|
-
overlay.style.zIndex =
|
|
935
|
-
const divisions = gridMode ===
|
|
912
|
+
const overlay = document.createElement('div');
|
|
913
|
+
overlay.style.position = 'absolute';
|
|
914
|
+
overlay.style.top = '0';
|
|
915
|
+
overlay.style.left = '0';
|
|
916
|
+
overlay.style.width = '100%';
|
|
917
|
+
overlay.style.height = '100%';
|
|
918
|
+
overlay.style.pointerEvents = 'none';
|
|
919
|
+
overlay.style.zIndex = '10';
|
|
920
|
+
const divisions = gridMode === '3x3' ? 3 : 4;
|
|
936
921
|
// Create SVG for grid lines
|
|
937
|
-
const svg = document.createElementNS(
|
|
938
|
-
svg.style.width =
|
|
939
|
-
svg.style.height =
|
|
940
|
-
svg.style.position =
|
|
941
|
-
svg.style.top =
|
|
942
|
-
svg.style.left =
|
|
922
|
+
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
923
|
+
svg.style.width = '100%';
|
|
924
|
+
svg.style.height = '100%';
|
|
925
|
+
svg.style.position = 'absolute';
|
|
926
|
+
svg.style.top = '0';
|
|
927
|
+
svg.style.left = '0';
|
|
943
928
|
// Create grid lines
|
|
944
929
|
for (let i = 1; i < divisions; i++) {
|
|
945
930
|
// Vertical lines
|
|
946
|
-
const verticalLine = document.createElementNS(
|
|
947
|
-
verticalLine.setAttribute(
|
|
948
|
-
verticalLine.setAttribute(
|
|
949
|
-
verticalLine.setAttribute(
|
|
950
|
-
verticalLine.setAttribute(
|
|
951
|
-
verticalLine.setAttribute(
|
|
952
|
-
verticalLine.setAttribute(
|
|
931
|
+
const verticalLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');
|
|
932
|
+
verticalLine.setAttribute('x1', `${(i / divisions) * 100}%`);
|
|
933
|
+
verticalLine.setAttribute('y1', '0%');
|
|
934
|
+
verticalLine.setAttribute('x2', `${(i / divisions) * 100}%`);
|
|
935
|
+
verticalLine.setAttribute('y2', '100%');
|
|
936
|
+
verticalLine.setAttribute('stroke', 'rgba(255, 255, 255, 0.5)');
|
|
937
|
+
verticalLine.setAttribute('stroke-width', '1');
|
|
953
938
|
svg.appendChild(verticalLine);
|
|
954
939
|
// Horizontal lines
|
|
955
|
-
const horizontalLine = document.createElementNS(
|
|
956
|
-
horizontalLine.setAttribute(
|
|
957
|
-
horizontalLine.setAttribute(
|
|
958
|
-
horizontalLine.setAttribute(
|
|
959
|
-
horizontalLine.setAttribute(
|
|
960
|
-
horizontalLine.setAttribute(
|
|
961
|
-
horizontalLine.setAttribute(
|
|
940
|
+
const horizontalLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');
|
|
941
|
+
horizontalLine.setAttribute('x1', '0%');
|
|
942
|
+
horizontalLine.setAttribute('y1', `${(i / divisions) * 100}%`);
|
|
943
|
+
horizontalLine.setAttribute('x2', '100%');
|
|
944
|
+
horizontalLine.setAttribute('y2', `${(i / divisions) * 100}%`);
|
|
945
|
+
horizontalLine.setAttribute('stroke', 'rgba(255, 255, 255, 0.5)');
|
|
946
|
+
horizontalLine.setAttribute('stroke-width', '1');
|
|
962
947
|
svg.appendChild(horizontalLine);
|
|
963
948
|
}
|
|
964
949
|
overlay.appendChild(svg);
|
|
@@ -971,12 +956,12 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
971
956
|
}
|
|
972
957
|
async getGridMode() {
|
|
973
958
|
// Web implementation - default to none
|
|
974
|
-
return { gridMode:
|
|
959
|
+
return { gridMode: 'none' };
|
|
975
960
|
}
|
|
976
961
|
async getPreviewSize() {
|
|
977
962
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
978
963
|
if (!video) {
|
|
979
|
-
throw new Error(
|
|
964
|
+
throw new Error('camera is not running');
|
|
980
965
|
}
|
|
981
966
|
const offsetX = video.width / 8;
|
|
982
967
|
const offsetY = video.height / 8;
|
|
@@ -990,7 +975,7 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
990
975
|
async setPreviewSize(options) {
|
|
991
976
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
992
977
|
if (!video) {
|
|
993
|
-
throw new Error(
|
|
978
|
+
throw new Error('camera is not running');
|
|
994
979
|
}
|
|
995
980
|
video.style.left = `${options.x}px`;
|
|
996
981
|
video.style.top = `${options.y}px`;
|
|
@@ -1008,16 +993,16 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
1008
993
|
async setFocus(options) {
|
|
1009
994
|
// Reject if values are outside 0-1 range
|
|
1010
995
|
if (options.x < 0 || options.x > 1 || options.y < 0 || options.y > 1) {
|
|
1011
|
-
throw new Error(
|
|
996
|
+
throw new Error('Focus coordinates must be between 0 and 1');
|
|
1012
997
|
}
|
|
1013
998
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
1014
999
|
if (!(video === null || video === void 0 ? void 0 : video.srcObject)) {
|
|
1015
|
-
throw new Error(
|
|
1000
|
+
throw new Error('camera is not running');
|
|
1016
1001
|
}
|
|
1017
1002
|
const stream = video.srcObject;
|
|
1018
1003
|
const videoTrack = stream.getVideoTracks()[0];
|
|
1019
1004
|
if (!videoTrack) {
|
|
1020
|
-
throw new Error(
|
|
1005
|
+
throw new Error('no video track found');
|
|
1021
1006
|
}
|
|
1022
1007
|
const capabilities = videoTrack.getCapabilities();
|
|
1023
1008
|
// Check if focusing is supported
|
|
@@ -1028,7 +1013,7 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
1028
1013
|
await videoTrack.applyConstraints({
|
|
1029
1014
|
advanced: [
|
|
1030
1015
|
{
|
|
1031
|
-
focusMode:
|
|
1016
|
+
focusMode: 'manual',
|
|
1032
1017
|
focusDistance: 0.5, // Mid-range focus as fallback
|
|
1033
1018
|
},
|
|
1034
1019
|
],
|
|
@@ -1039,32 +1024,32 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
1039
1024
|
}
|
|
1040
1025
|
}
|
|
1041
1026
|
else {
|
|
1042
|
-
console.warn(
|
|
1027
|
+
console.warn('Focus control is not supported on this device. Focus coordinates were provided but cannot be applied.');
|
|
1043
1028
|
}
|
|
1044
1029
|
}
|
|
1045
1030
|
// Exposure stubs (unsupported on web)
|
|
1046
1031
|
async getExposureModes() {
|
|
1047
|
-
throw new Error(
|
|
1032
|
+
throw new Error('getExposureModes not supported under the web platform');
|
|
1048
1033
|
}
|
|
1049
1034
|
async getExposureMode() {
|
|
1050
|
-
throw new Error(
|
|
1035
|
+
throw new Error('getExposureMode not supported under the web platform');
|
|
1051
1036
|
}
|
|
1052
1037
|
async setExposureMode(_options) {
|
|
1053
|
-
throw new Error(
|
|
1038
|
+
throw new Error('setExposureMode not supported under the web platform');
|
|
1054
1039
|
}
|
|
1055
1040
|
async getExposureCompensationRange() {
|
|
1056
|
-
throw new Error(
|
|
1041
|
+
throw new Error('getExposureCompensationRange not supported under the web platform');
|
|
1057
1042
|
}
|
|
1058
1043
|
async getExposureCompensation() {
|
|
1059
|
-
throw new Error(
|
|
1044
|
+
throw new Error('getExposureCompensation not supported under the web platform');
|
|
1060
1045
|
}
|
|
1061
1046
|
async setExposureCompensation(_options) {
|
|
1062
|
-
throw new Error(
|
|
1047
|
+
throw new Error('setExposureCompensation not supported under the web platform');
|
|
1063
1048
|
}
|
|
1064
1049
|
async deleteFile(_options) {
|
|
1065
1050
|
// Mark parameter as intentionally unused to satisfy linter
|
|
1066
1051
|
void _options;
|
|
1067
|
-
throw new Error(
|
|
1052
|
+
throw new Error('deleteFile not supported under the web platform');
|
|
1068
1053
|
}
|
|
1069
1054
|
}
|
|
1070
1055
|
//# sourceMappingURL=web.js.map
|