@capgo/camera-preview 8.3.7 → 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.
@@ -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 = viewportWidth;
401
- let targetHeight = targetWidth / targetRatio;
402
- // If height exceeds viewport, fit to height instead
403
- if (targetHeight > viewportHeight) {
404
- targetHeight = viewportHeight;
405
- targetWidth = targetHeight * targetRatio;
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
- return {
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