@capgo/camera-preview 8.3.6 → 8.3.8-beta.pr356.31.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +188 -36
- package/android/build.gradle +1 -0
- package/android/src/main/AndroidManifest.xml +5 -0
- package/android/src/main/java/app/capgo/capacitor/camera/preview/CameraPreview.java +337 -9
- package/android/src/main/java/app/capgo/capacitor/camera/preview/CameraXView.java +280 -0
- package/dist/docs.json +345 -11
- package/dist/esm/definitions.d.ts +83 -7
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +13 -3
- package/dist/esm/web.js +169 -9
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +169 -9
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +169 -9
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CapgoCameraPreviewPlugin/CameraController.swift +167 -1
- package/ios/Sources/CapgoCameraPreviewPlugin/Plugin.swift +72 -3
- package/package.json +7 -3
package/dist/plugin.cjs.js
CHANGED
|
@@ -55,6 +55,10 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
55
55
|
this.orientationListenerBound = false;
|
|
56
56
|
this.mediaRecorder = null;
|
|
57
57
|
this.recordedChunks = [];
|
|
58
|
+
this.currentAspectRatio = '4:3';
|
|
59
|
+
this.barcodeDetector = null;
|
|
60
|
+
this.barcodeScannerTimer = null;
|
|
61
|
+
this.barcodeScannerBusy = false;
|
|
58
62
|
}
|
|
59
63
|
async checkPermissions(options) {
|
|
60
64
|
const result = {
|
|
@@ -242,6 +246,7 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
242
246
|
// Default to 4:3 if no aspect ratio or size specified
|
|
243
247
|
const useDefaultAspectRatio = !options.aspectRatio && !options.width && !options.height;
|
|
244
248
|
const effectiveAspectRatio = options.aspectRatio || (useDefaultAspectRatio ? '4:3' : null);
|
|
249
|
+
this.currentAspectRatio = effectiveAspectRatio || '4:3';
|
|
245
250
|
if (options.width) {
|
|
246
251
|
this.videoElement.width = options.width;
|
|
247
252
|
this.videoElement.style.width = `${options.width}px`;
|
|
@@ -393,16 +398,24 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
393
398
|
}
|
|
394
399
|
else if (effectiveAspectRatio) {
|
|
395
400
|
// Aspect ratio specified but no size
|
|
396
|
-
const [widthRatio, heightRatio] = effectiveAspectRatio.split(':').map(Number);
|
|
397
|
-
const targetRatio = widthRatio / heightRatio;
|
|
398
401
|
const viewportWidth = window.innerWidth;
|
|
399
402
|
const viewportHeight = window.innerHeight;
|
|
400
|
-
let targetWidth
|
|
401
|
-
let targetHeight
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
targetHeight =
|
|
405
|
-
|
|
403
|
+
let targetWidth;
|
|
404
|
+
let targetHeight;
|
|
405
|
+
if (effectiveAspectRatio === 'fill') {
|
|
406
|
+
targetWidth = containerWidth;
|
|
407
|
+
targetHeight = containerHeight;
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
const [widthRatio, heightRatio] = effectiveAspectRatio.split(':').map(Number);
|
|
411
|
+
const targetRatio = widthRatio / heightRatio;
|
|
412
|
+
targetWidth = viewportWidth;
|
|
413
|
+
targetHeight = targetWidth / targetRatio;
|
|
414
|
+
// If height exceeds viewport, fit to height instead
|
|
415
|
+
if (targetHeight > viewportHeight) {
|
|
416
|
+
targetHeight = viewportHeight;
|
|
417
|
+
targetWidth = targetHeight * targetRatio;
|
|
418
|
+
}
|
|
406
419
|
}
|
|
407
420
|
this.videoElement.width = targetWidth;
|
|
408
421
|
this.videoElement.height = targetHeight;
|
|
@@ -530,12 +543,23 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
530
543
|
height: computedStyle.height,
|
|
531
544
|
},
|
|
532
545
|
});
|
|
533
|
-
|
|
546
|
+
const result = {
|
|
534
547
|
width: Math.round(rect.width),
|
|
535
548
|
height: Math.round(rect.height),
|
|
536
549
|
x: Math.round(rect.x),
|
|
537
550
|
y: Math.round(rect.y),
|
|
538
551
|
};
|
|
552
|
+
const barcodeScannerOptions = this.getStartBarcodeScannerOptions(options);
|
|
553
|
+
if (barcodeScannerOptions) {
|
|
554
|
+
try {
|
|
555
|
+
await this.startBarcodeScanner(barcodeScannerOptions);
|
|
556
|
+
}
|
|
557
|
+
catch (error) {
|
|
558
|
+
await this.stop({ force: true });
|
|
559
|
+
throw error;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
return result;
|
|
539
563
|
}
|
|
540
564
|
stopStream(stream) {
|
|
541
565
|
if (stream) {
|
|
@@ -545,6 +569,7 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
545
569
|
}
|
|
546
570
|
}
|
|
547
571
|
async stop(_options) {
|
|
572
|
+
await this.stopBarcodeScanner();
|
|
548
573
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
549
574
|
if (video) {
|
|
550
575
|
video.pause();
|
|
@@ -632,6 +657,116 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
632
657
|
async captureSample(_options) {
|
|
633
658
|
return this.capture(_options);
|
|
634
659
|
}
|
|
660
|
+
async startBarcodeScanner(options) {
|
|
661
|
+
var _a, _b;
|
|
662
|
+
if (!this.isStarted || !((_a = this.videoElement) === null || _a === void 0 ? void 0 : _a.srcObject)) {
|
|
663
|
+
throw new Error('camera is not running');
|
|
664
|
+
}
|
|
665
|
+
const Detector = window.BarcodeDetector;
|
|
666
|
+
if (!Detector) {
|
|
667
|
+
throw new Error('BarcodeDetector API is not available in this browser');
|
|
668
|
+
}
|
|
669
|
+
await this.stopBarcodeScanner();
|
|
670
|
+
const formats = ((options === null || options === void 0 ? void 0 : options.formats) || [])
|
|
671
|
+
.map((format) => this.toWebBarcodeFormat(format))
|
|
672
|
+
.filter((format) => !!format);
|
|
673
|
+
this.barcodeDetector = new Detector(formats.length > 0 ? { formats } : undefined);
|
|
674
|
+
const detectionInterval = Math.max(100, (_b = options === null || options === void 0 ? void 0 : options.detectionInterval) !== null && _b !== void 0 ? _b : 500);
|
|
675
|
+
const detect = async () => {
|
|
676
|
+
var _a;
|
|
677
|
+
if (this.barcodeScannerBusy || !this.barcodeDetector || !((_a = this.videoElement) === null || _a === void 0 ? void 0 : _a.srcObject)) {
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
this.barcodeScannerBusy = true;
|
|
681
|
+
try {
|
|
682
|
+
const results = (await this.barcodeDetector.detect(this.videoElement));
|
|
683
|
+
const barcodes = results
|
|
684
|
+
.map((result) => this.toBarcodeScanResult(result))
|
|
685
|
+
.filter((barcode) => !!barcode);
|
|
686
|
+
if (barcodes.length > 0) {
|
|
687
|
+
this.notifyListeners('barcodeScanned', { barcodes });
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
catch (error) {
|
|
691
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
692
|
+
console.error('Barcode detection failed:', error);
|
|
693
|
+
this.notifyListeners('barcodeScanError', { message });
|
|
694
|
+
}
|
|
695
|
+
finally {
|
|
696
|
+
this.barcodeScannerBusy = false;
|
|
697
|
+
}
|
|
698
|
+
};
|
|
699
|
+
this.barcodeScannerTimer = window.setInterval(() => {
|
|
700
|
+
void detect();
|
|
701
|
+
}, detectionInterval);
|
|
702
|
+
void detect();
|
|
703
|
+
}
|
|
704
|
+
async stopBarcodeScanner() {
|
|
705
|
+
if (this.barcodeScannerTimer !== null) {
|
|
706
|
+
window.clearInterval(this.barcodeScannerTimer);
|
|
707
|
+
this.barcodeScannerTimer = null;
|
|
708
|
+
}
|
|
709
|
+
this.barcodeDetector = null;
|
|
710
|
+
this.barcodeScannerBusy = false;
|
|
711
|
+
}
|
|
712
|
+
getStartBarcodeScannerOptions(options) {
|
|
713
|
+
if (options.barcodeScanner === true) {
|
|
714
|
+
return {};
|
|
715
|
+
}
|
|
716
|
+
if (options.barcodeScanner && typeof options.barcodeScanner === 'object') {
|
|
717
|
+
return options.barcodeScanner;
|
|
718
|
+
}
|
|
719
|
+
return null;
|
|
720
|
+
}
|
|
721
|
+
toWebBarcodeFormat(format) {
|
|
722
|
+
switch (format) {
|
|
723
|
+
case 'aztec':
|
|
724
|
+
case 'codabar':
|
|
725
|
+
case 'code_39':
|
|
726
|
+
case 'code_93':
|
|
727
|
+
case 'code_128':
|
|
728
|
+
case 'data_matrix':
|
|
729
|
+
case 'ean_8':
|
|
730
|
+
case 'ean_13':
|
|
731
|
+
case 'itf':
|
|
732
|
+
case 'pdf417':
|
|
733
|
+
case 'qr_code':
|
|
734
|
+
case 'upc_a':
|
|
735
|
+
case 'upc_e':
|
|
736
|
+
return format;
|
|
737
|
+
default:
|
|
738
|
+
return null;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
toBarcodeScanResult(result) {
|
|
742
|
+
if (!result.rawValue) {
|
|
743
|
+
return null;
|
|
744
|
+
}
|
|
745
|
+
return {
|
|
746
|
+
value: result.rawValue,
|
|
747
|
+
format: this.fromWebBarcodeFormat(result.format),
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
fromWebBarcodeFormat(format) {
|
|
751
|
+
switch (format) {
|
|
752
|
+
case 'aztec':
|
|
753
|
+
case 'codabar':
|
|
754
|
+
case 'code_39':
|
|
755
|
+
case 'code_93':
|
|
756
|
+
case 'code_128':
|
|
757
|
+
case 'data_matrix':
|
|
758
|
+
case 'ean_8':
|
|
759
|
+
case 'ean_13':
|
|
760
|
+
case 'itf':
|
|
761
|
+
case 'pdf417':
|
|
762
|
+
case 'qr_code':
|
|
763
|
+
case 'upc_a':
|
|
764
|
+
case 'upc_e':
|
|
765
|
+
return format;
|
|
766
|
+
default:
|
|
767
|
+
return 'unknown';
|
|
768
|
+
}
|
|
769
|
+
}
|
|
635
770
|
async stopRecordVideo() {
|
|
636
771
|
if (!this.mediaRecorder) {
|
|
637
772
|
throw new Error('video recording is not running');
|
|
@@ -949,6 +1084,9 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
949
1084
|
if (!video) {
|
|
950
1085
|
throw new Error('camera is not running');
|
|
951
1086
|
}
|
|
1087
|
+
if (this.currentAspectRatio === 'fill') {
|
|
1088
|
+
return { aspectRatio: 'fill' };
|
|
1089
|
+
}
|
|
952
1090
|
const width = video.offsetWidth;
|
|
953
1091
|
const height = video.offsetHeight;
|
|
954
1092
|
if (width && height) {
|
|
@@ -965,10 +1103,32 @@ class CameraPreviewWeb extends core.WebPlugin {
|
|
|
965
1103
|
return { aspectRatio: '4:3' };
|
|
966
1104
|
}
|
|
967
1105
|
async setAspectRatio(options) {
|
|
1106
|
+
var _a, _b;
|
|
968
1107
|
const video = document.getElementById(DEFAULT_VIDEO_ID);
|
|
969
1108
|
if (!video) {
|
|
970
1109
|
throw new Error('camera is not running');
|
|
971
1110
|
}
|
|
1111
|
+
this.currentAspectRatio = options.aspectRatio;
|
|
1112
|
+
if (options.aspectRatio === 'fill') {
|
|
1113
|
+
const parent = video.parentElement || document.body;
|
|
1114
|
+
const targetWidth = parent.clientWidth || window.innerWidth;
|
|
1115
|
+
const targetHeight = parent.clientHeight || window.innerHeight;
|
|
1116
|
+
const x = (_a = options.x) !== null && _a !== void 0 ? _a : 0;
|
|
1117
|
+
const y = (_b = options.y) !== null && _b !== void 0 ? _b : 0;
|
|
1118
|
+
video.style.width = `${targetWidth}px`;
|
|
1119
|
+
video.style.height = `${targetHeight}px`;
|
|
1120
|
+
video.style.left = `${x}px`;
|
|
1121
|
+
video.style.top = `${y}px`;
|
|
1122
|
+
video.style.position = 'absolute';
|
|
1123
|
+
const offsetX = targetWidth / 8;
|
|
1124
|
+
const offsetY = targetHeight / 8;
|
|
1125
|
+
return {
|
|
1126
|
+
width: Math.round(targetWidth),
|
|
1127
|
+
height: Math.round(targetHeight),
|
|
1128
|
+
x: Math.round(x + offsetX),
|
|
1129
|
+
y: Math.round(y + offsetY),
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
972
1132
|
if (options.aspectRatio) {
|
|
973
1133
|
const [widthRatio, heightRatio] = options.aspectRatio.split(':').map(Number);
|
|
974
1134
|
// For camera, use portrait orientation: 4:3 becomes 3:4, 16:9 becomes 9:16
|