ngx-image-cropper 9.1.2 → 9.1.4

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.
@@ -268,7 +268,7 @@ class CropperState {
268
268
  roundCropper: false,
269
269
  onlyScaleDown: false,
270
270
  imageQuality: 92,
271
- backgroundColor: null,
271
+ backgroundColor: undefined,
272
272
  containWithinAspectRatio: false,
273
273
  hideResizeSquares: false,
274
274
  alignImage: 'center',
@@ -429,6 +429,15 @@ class CropperState {
429
429
  y2: this.maxSize().height
430
430
  };
431
431
  }
432
+ toCropInput() {
433
+ return {
434
+ cropper: this.cropper(),
435
+ maxSize: this.maxSize(),
436
+ transform: this.transform,
437
+ loadedImage: this.loadedImage,
438
+ options: { ...this.options }
439
+ };
440
+ }
432
441
  }
433
442
 
434
443
  var MoveTypes;
@@ -519,8 +528,8 @@ function percentage(percent, totalValue) {
519
528
  }
520
529
 
521
530
  class CropService {
522
- crop(cropperState, output) {
523
- const imagePosition = this.getImagePosition(cropperState);
531
+ crop(input, output) {
532
+ const imagePosition = this.getImagePosition(input);
524
533
  const width = imagePosition.x2 - imagePosition.x1;
525
534
  const height = imagePosition.y2 - imagePosition.y1;
526
535
  const cropCanvas = document.createElement('canvas');
@@ -530,77 +539,76 @@ class CropService {
530
539
  if (!ctx) {
531
540
  return null;
532
541
  }
533
- if (cropperState.options.backgroundColor != null) {
534
- ctx.fillStyle = cropperState.options.backgroundColor;
542
+ if (input.options?.backgroundColor != null) {
543
+ ctx.fillStyle = input.options.backgroundColor;
535
544
  ctx.fillRect(0, 0, width, height);
536
545
  }
537
- const scaleX = (cropperState.transform.scale || 1) * (cropperState.transform.flipH ? -1 : 1);
538
- const scaleY = (cropperState.transform.scale || 1) * (cropperState.transform.flipV ? -1 : 1);
539
- const { translateH, translateV } = this.getCanvasTranslate(cropperState);
540
- const transformedImage = cropperState.loadedImage.transformed;
546
+ const scaleX = (input.transform?.scale || 1) * (input.transform?.flipH ? -1 : 1);
547
+ const scaleY = (input.transform?.scale || 1) * (input.transform?.flipV ? -1 : 1);
548
+ const { translateH, translateV } = this.getCanvasTranslate(input);
549
+ const transformedImage = input.loadedImage.transformed;
541
550
  ctx.setTransform(scaleX, 0, 0, scaleY, transformedImage.size.width / 2 + translateH, transformedImage.size.height / 2 + translateV);
542
551
  ctx.translate(-imagePosition.x1 / scaleX, -imagePosition.y1 / scaleY);
543
- ctx.rotate((cropperState.transform.rotate || 0) * Math.PI / 180);
552
+ ctx.rotate((input.transform?.rotate || 0) * Math.PI / 180);
544
553
  ctx.drawImage(transformedImage.image, -transformedImage.size.width / 2, -transformedImage.size.height / 2);
545
554
  const result = {
546
555
  width, height,
547
556
  imagePosition,
548
- cropperPosition: { ...cropperState.cropper() }
557
+ cropperPosition: { ...input.cropper }
549
558
  };
550
- if (cropperState.options.containWithinAspectRatio) {
551
- result.offsetImagePosition = this.getOffsetImagePosition(cropperState);
559
+ if (input.options?.containWithinAspectRatio) {
560
+ result.offsetImagePosition = this.getOffsetImagePosition(input);
552
561
  }
553
- const resizeRatio = this.getResizeRatio(width, height, cropperState.options);
562
+ const resizeRatio = this.getResizeRatio(width, height, input.options);
554
563
  if (resizeRatio !== 1) {
555
564
  result.width = Math.round(width * resizeRatio);
556
- result.height = cropperState.options.maintainAspectRatio
557
- ? Math.round(result.width / cropperState.options.aspectRatio)
565
+ result.height = input.options?.maintainAspectRatio
566
+ ? Math.round(result.width / (input.options?.aspectRatio ?? 1))
558
567
  : Math.round(height * resizeRatio);
559
568
  resizeCanvas(cropCanvas, result.width, result.height);
560
569
  }
561
570
  if (output === 'blob') {
562
- return this.cropToBlob(result, cropCanvas, cropperState);
571
+ return this.cropToBlob(result, cropCanvas, input);
563
572
  }
564
573
  else {
565
- result.base64 = cropCanvas.toDataURL('image/' + cropperState.options.format, this.getQuality(cropperState.options));
574
+ result.base64 = cropCanvas.toDataURL('image/' + (input.options?.format ?? 'png'), this.getQuality(input.options));
566
575
  return result;
567
576
  }
568
577
  }
569
- async cropToBlob(output, cropCanvas, cropperState) {
570
- output.blob = await new Promise(resolve => cropCanvas.toBlob(resolve, 'image/' + cropperState.options.format, this.getQuality(cropperState.options)));
578
+ async cropToBlob(output, cropCanvas, input) {
579
+ output.blob = await new Promise(resolve => cropCanvas.toBlob(resolve, 'image/' + (input.options?.format ?? 'png'), this.getQuality(input.options)));
571
580
  if (output.blob) {
572
581
  output.objectUrl = URL.createObjectURL(output.blob);
573
582
  }
574
583
  return output;
575
584
  }
576
- getCanvasTranslate(cropperState) {
577
- if (cropperState.transform.translateUnit === 'px') {
578
- const ratio = this.getRatio(cropperState);
585
+ getCanvasTranslate(input) {
586
+ if (input.transform?.translateUnit === 'px') {
587
+ const ratio = this.getRatio(input);
579
588
  return {
580
- translateH: (cropperState.transform.translateH || 0) * ratio,
581
- translateV: (cropperState.transform.translateV || 0) * ratio
589
+ translateH: (input.transform?.translateH || 0) * ratio,
590
+ translateV: (input.transform?.translateV || 0) * ratio
582
591
  };
583
592
  }
584
593
  else {
585
594
  return {
586
- translateH: cropperState.transform.translateH ? percentage(cropperState.transform.translateH, cropperState.loadedImage.transformed.size.width) : 0,
587
- translateV: cropperState.transform.translateV ? percentage(cropperState.transform.translateV, cropperState.loadedImage.transformed.size.height) : 0
595
+ translateH: input.transform?.translateH ? percentage(input.transform.translateH, input.loadedImage.transformed.size.width) : 0,
596
+ translateV: input.transform?.translateV ? percentage(input.transform.translateV, input.loadedImage.transformed.size.height) : 0
588
597
  };
589
598
  }
590
599
  }
591
- getRatio(cropperState) {
592
- return cropperState.loadedImage.transformed.size.width / cropperState.maxSize().width;
600
+ getRatio(input) {
601
+ return input.loadedImage.transformed.size.width / input.maxSize.width;
593
602
  }
594
603
  getImagePosition(cropperState) {
595
604
  const ratio = this.getRatio(cropperState);
596
- const cropper = cropperState.cropper();
597
605
  const out = {
598
- x1: Math.round(cropper.x1 * ratio),
599
- y1: Math.round(cropper.y1 * ratio),
600
- x2: Math.round(cropper.x2 * ratio),
601
- y2: Math.round(cropper.y2 * ratio)
606
+ x1: Math.round(cropperState.cropper.x1 * ratio),
607
+ y1: Math.round(cropperState.cropper.y1 * ratio),
608
+ x2: Math.round(cropperState.cropper.x2 * ratio),
609
+ y2: Math.round(cropperState.cropper.y2 * ratio)
602
610
  };
603
- if (!cropperState.options.containWithinAspectRatio) {
611
+ if (!cropperState.options?.containWithinAspectRatio) {
604
612
  out.x1 = Math.max(out.x1, 0);
605
613
  out.y1 = Math.max(out.y1, 0);
606
614
  out.x2 = Math.min(out.x2, cropperState.loadedImage.transformed.size.width);
@@ -608,52 +616,50 @@ class CropService {
608
616
  }
609
617
  return out;
610
618
  }
611
- getOffsetImagePosition(cropperState) {
612
- const canvasRotation = cropperState.options.canvasRotation + cropperState.loadedImage.exifTransform.rotate;
613
- const ratio = this.getRatio(cropperState);
619
+ getOffsetImagePosition(input) {
620
+ const canvasRotation = (input.options?.canvasRotation ?? 0) + input.loadedImage.exifTransform.rotate;
621
+ const ratio = this.getRatio(input);
614
622
  let offsetX;
615
623
  let offsetY;
616
624
  if (canvasRotation % 2) {
617
- offsetX = (cropperState.loadedImage.transformed.size.width - cropperState.loadedImage.original.size.height) / 2;
618
- offsetY = (cropperState.loadedImage.transformed.size.height - cropperState.loadedImage.original.size.width) / 2;
625
+ offsetX = (input.loadedImage.transformed.size.width - input.loadedImage.original.size.height) / 2;
626
+ offsetY = (input.loadedImage.transformed.size.height - input.loadedImage.original.size.width) / 2;
619
627
  }
620
628
  else {
621
- offsetX = (cropperState.loadedImage.transformed.size.width - cropperState.loadedImage.original.size.width) / 2;
622
- offsetY = (cropperState.loadedImage.transformed.size.height - cropperState.loadedImage.original.size.height) / 2;
629
+ offsetX = (input.loadedImage.transformed.size.width - input.loadedImage.original.size.width) / 2;
630
+ offsetY = (input.loadedImage.transformed.size.height - input.loadedImage.original.size.height) / 2;
623
631
  }
624
- const cropper = cropperState.cropper();
632
+ const cropper = input.cropper;
625
633
  const out = {
626
634
  x1: Math.round(cropper.x1 * ratio) - offsetX,
627
635
  y1: Math.round(cropper.y1 * ratio) - offsetY,
628
636
  x2: Math.round(cropper.x2 * ratio) - offsetX,
629
637
  y2: Math.round(cropper.y2 * ratio) - offsetY
630
638
  };
631
- if (!cropperState.options.containWithinAspectRatio) {
639
+ if (!input.options?.containWithinAspectRatio) {
632
640
  out.x1 = Math.max(out.x1, 0);
633
641
  out.y1 = Math.max(out.y1, 0);
634
- out.x2 = Math.min(out.x2, cropperState.loadedImage.transformed.size.width);
635
- out.y2 = Math.min(out.y2, cropperState.loadedImage.transformed.size.height);
642
+ out.x2 = Math.min(out.x2, input.loadedImage.transformed.size.width);
643
+ out.y2 = Math.min(out.y2, input.loadedImage.transformed.size.height);
636
644
  }
637
645
  return out;
638
646
  }
639
647
  getResizeRatio(width, height, options) {
640
- const ratioWidth = options.resizeToWidth / width;
641
- const ratioHeight = options.resizeToHeight / height;
642
648
  const ratios = new Array();
643
- if (options.resizeToWidth > 0) {
644
- ratios.push(ratioWidth);
649
+ if (options?.resizeToWidth && options.resizeToWidth > 0) {
650
+ ratios.push(options.resizeToWidth / width);
645
651
  }
646
- if (options.resizeToHeight > 0) {
647
- ratios.push(ratioHeight);
652
+ if (options?.resizeToHeight && options.resizeToHeight > 0) {
653
+ ratios.push(options.resizeToHeight / height);
648
654
  }
649
655
  const result = ratios.length === 0 ? 1 : Math.min(...ratios);
650
- if (result > 1 && !options.onlyScaleDown) {
656
+ if (result > 1 && !options?.onlyScaleDown) {
651
657
  return result;
652
658
  }
653
659
  return Math.min(result, 1);
654
660
  }
655
661
  getQuality(options) {
656
- return Math.min(1, Math.max(0, options.imageQuality / 100));
662
+ return Math.min(1, Math.max(0, options?.imageQuality ?? 92 / 100));
657
663
  }
658
664
  }
659
665
 
@@ -736,31 +742,31 @@ class LoadImageService {
736
742
  constructor() {
737
743
  this.autoRotateSupported = supportsAutomaticRotation();
738
744
  }
739
- async loadImageFile(file, cropperSettings) {
745
+ async loadImageFile(file, options) {
740
746
  const arrayBuffer = await file.arrayBuffer();
741
- if (cropperSettings.options.checkImageType) {
742
- return await this.checkImageTypeAndLoadImageFromArrayBuffer(arrayBuffer, file.type, cropperSettings);
747
+ if (options.checkImageType) {
748
+ return await this.checkImageTypeAndLoadImageFromArrayBuffer(arrayBuffer, file.type, options);
743
749
  }
744
- return await this.loadImageFromArrayBuffer(arrayBuffer, cropperSettings);
750
+ return await this.loadImageFromArrayBuffer(arrayBuffer, options);
745
751
  }
746
- checkImageTypeAndLoadImageFromArrayBuffer(arrayBuffer, imageType, cropperSettings) {
752
+ checkImageTypeAndLoadImageFromArrayBuffer(arrayBuffer, imageType, options) {
747
753
  if (!this.isValidImageType(imageType)) {
748
754
  return Promise.reject(new Error('Invalid image type'));
749
755
  }
750
- return this.loadImageFromArrayBuffer(arrayBuffer, cropperSettings, imageType);
756
+ return this.loadImageFromArrayBuffer(arrayBuffer, options, imageType);
751
757
  }
752
758
  isValidImageType(type) {
753
759
  return /image\/(png|jpg|jpeg|heic|bmp|gif|tiff|svg|webp|x-icon|vnd.microsoft.icon)/.test(type);
754
760
  }
755
- async loadImageFromURL(url, cropperSettings) {
761
+ async loadImageFromURL(url, options) {
756
762
  const res = await fetch(url);
757
763
  const blob = await res.blob();
758
764
  const buffer = await blob.arrayBuffer();
759
- return await this.loadImageFromArrayBuffer(buffer, cropperSettings, blob.type);
765
+ return await this.loadImageFromArrayBuffer(buffer, options, blob.type);
760
766
  }
761
- loadBase64Image(imageBase64, cropperSettings) {
767
+ loadBase64Image(imageBase64, options) {
762
768
  const arrayBuffer = this.base64ToArrayBuffer(imageBase64);
763
- return this.loadImageFromArrayBuffer(arrayBuffer, cropperSettings);
769
+ return this.loadImageFromArrayBuffer(arrayBuffer, options);
764
770
  }
765
771
  base64ToArrayBuffer(imageBase64) {
766
772
  imageBase64 = imageBase64.replace(/^data:([^;]+);base64,/gmi, '');
@@ -772,7 +778,7 @@ class LoadImageService {
772
778
  }
773
779
  return bytes.buffer;
774
780
  }
775
- async loadImageFromArrayBuffer(arrayBuffer, cropperState, imageType) {
781
+ async loadImageFromArrayBuffer(arrayBuffer, options, imageType) {
776
782
  const res = await new Promise(async (resolve, reject) => {
777
783
  try {
778
784
  const blob = new Blob([arrayBuffer], imageType ? { type: imageType } : undefined);
@@ -793,7 +799,7 @@ class LoadImageService {
793
799
  reject(e);
794
800
  }
795
801
  });
796
- return await this.transformImageFromArrayBuffer(res, cropperState, res.originalImageSize != null);
802
+ return await this.transformImageFromArrayBuffer(res, options, res.originalImageSize != null);
797
803
  }
798
804
  async getSvgImageSize(blob) {
799
805
  const parser = new DOMParser();
@@ -818,7 +824,7 @@ class LoadImageService {
818
824
  }
819
825
  throw Error('Failed to load SVG image. SVG must have width + height or viewBox definition.');
820
826
  }
821
- async transformImageFromArrayBuffer(res, cropperSettings, forceTransform = false) {
827
+ async transformImageFromArrayBuffer(res, options, forceTransform = false) {
822
828
  const autoRotate = await this.autoRotateSupported;
823
829
  const exifTransform = getTransformationsFromExifData(autoRotate ? -1 : res.originalArrayBuffer);
824
830
  if (!res.originalImage || !res.originalImage.complete) {
@@ -835,12 +841,12 @@ class LoadImageService {
835
841
  },
836
842
  exifTransform
837
843
  };
838
- return this.transformLoadedImage(loadedImage, cropperSettings, forceTransform);
844
+ return this.transformLoadedImage(loadedImage, options, forceTransform);
839
845
  }
840
- async transformLoadedImage(loadedImage, cropperState, forceTransform = false) {
841
- const canvasRotation = cropperState.options.canvasRotation + loadedImage.exifTransform.rotate;
846
+ async transformLoadedImage(loadedImage, options, forceTransform = false) {
847
+ const canvasRotation = (options.canvasRotation ?? 0) + loadedImage.exifTransform.rotate;
842
848
  const originalSize = loadedImage.original.size;
843
- if (!forceTransform && canvasRotation === 0 && !loadedImage.exifTransform.flip && !cropperState.options.containWithinAspectRatio) {
849
+ if (!forceTransform && canvasRotation === 0 && !loadedImage.exifTransform.flip && !options.containWithinAspectRatio) {
844
850
  return {
845
851
  original: {
846
852
  objectUrl: loadedImage.original.objectUrl,
@@ -855,7 +861,7 @@ class LoadImageService {
855
861
  exifTransform: loadedImage.exifTransform
856
862
  };
857
863
  }
858
- const transformedSize = this.getTransformedSize(originalSize, loadedImage.exifTransform, cropperState);
864
+ const transformedSize = this.getTransformedSize(originalSize, loadedImage.exifTransform, options);
859
865
  const canvas = document.createElement('canvas');
860
866
  canvas.width = transformedSize.width;
861
867
  canvas.height = transformedSize.height;
@@ -863,7 +869,7 @@ class LoadImageService {
863
869
  ctx?.setTransform(loadedImage.exifTransform.flip ? -1 : 1, 0, 0, 1, canvas.width / 2, canvas.height / 2);
864
870
  ctx?.rotate(Math.PI * (canvasRotation / 2));
865
871
  ctx?.drawImage(loadedImage.original.image, -originalSize.width / 2, -originalSize.height / 2);
866
- const blob = await new Promise(resolve => canvas.toBlob(resolve, cropperState.options.format));
872
+ const blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/' + (options.format ?? 'png')));
867
873
  if (!blob) {
868
874
  throw new Error('Failed to get Blob for transformed image.');
869
875
  }
@@ -894,20 +900,20 @@ class LoadImageService {
894
900
  image.src = objectUrl;
895
901
  }));
896
902
  }
897
- getTransformedSize(originalSize, exifTransform, cropperState) {
898
- const canvasRotation = cropperState.options.canvasRotation + exifTransform.rotate;
899
- if (cropperState.options.containWithinAspectRatio) {
903
+ getTransformedSize(originalSize, exifTransform, options) {
904
+ const canvasRotation = (options.canvasRotation ?? 0) + exifTransform.rotate;
905
+ if (options.containWithinAspectRatio) {
900
906
  if (canvasRotation % 2) {
901
- const minWidthToContain = originalSize.width * cropperState.options.aspectRatio;
902
- const minHeightToContain = originalSize.height / cropperState.options.aspectRatio;
907
+ const minWidthToContain = originalSize.width * (options.aspectRatio ?? 1);
908
+ const minHeightToContain = originalSize.height / (options.aspectRatio ?? 1);
903
909
  return {
904
910
  width: Math.max(originalSize.height, minWidthToContain),
905
911
  height: Math.max(originalSize.width, minHeightToContain)
906
912
  };
907
913
  }
908
914
  else {
909
- const minWidthToContain = originalSize.height * cropperState.options.aspectRatio;
910
- const minHeightToContain = originalSize.width / cropperState.options.aspectRatio;
915
+ const minWidthToContain = originalSize.height * (options.aspectRatio ?? 1);
916
+ const minHeightToContain = originalSize.width / (options.aspectRatio ?? 1);
911
917
  return {
912
918
  width: Math.max(originalSize.width, minWidthToContain),
913
919
  height: Math.max(originalSize.height, minHeightToContain)
@@ -1015,7 +1021,7 @@ class ImageCropperComponent {
1015
1021
  }
1016
1022
  if ((this.containWithinAspectRatio && changes['aspectRatio']) || changes['containWithinAspectRatio'] || changes['canvasRotation']) {
1017
1023
  this.loadImageService
1018
- .transformLoadedImage(this.state.loadedImage, this.state)
1024
+ .transformLoadedImage(this.state.loadedImage, this.state.options)
1019
1025
  .then((res) => this.setLoadedImage(res))
1020
1026
  .catch((err) => this.loadImageError(err));
1021
1027
  return;
@@ -1067,19 +1073,19 @@ class ImageCropperComponent {
1067
1073
  }
1068
1074
  loadImageFile(file) {
1069
1075
  this.loadImageService
1070
- .loadImageFile(file, this.state)
1076
+ .loadImageFile(file, this.state.options)
1071
1077
  .then((res) => this.setLoadedImage(res))
1072
1078
  .catch((err) => this.loadImageError(err));
1073
1079
  }
1074
1080
  loadBase64Image(imageBase64) {
1075
1081
  this.loadImageService
1076
- .loadBase64Image(imageBase64, this.state)
1082
+ .loadBase64Image(imageBase64, this.state.options)
1077
1083
  .then((res) => this.setLoadedImage(res))
1078
1084
  .catch((err) => this.loadImageError(err));
1079
1085
  }
1080
1086
  loadImageFromURL(url) {
1081
1087
  this.loadImageService
1082
- .loadImageFromURL(url, this.state)
1088
+ .loadImageFromURL(url, this.state.options)
1083
1089
  .then((res) => this.setLoadedImage(res))
1084
1090
  .catch((err) => this.loadImageError(err));
1085
1091
  }
@@ -1325,7 +1331,7 @@ class ImageCropperComponent {
1325
1331
  }
1326
1332
  cropToBlob() {
1327
1333
  return new Promise(async (resolve, reject) => {
1328
- const result = await this.cropService.crop(this.state, 'blob');
1334
+ const result = await this.cropService.crop(this.state.toCropInput(), 'blob');
1329
1335
  if (result) {
1330
1336
  this.imageCropped.emit(result);
1331
1337
  resolve(result);
@@ -1336,7 +1342,7 @@ class ImageCropperComponent {
1336
1342
  });
1337
1343
  }
1338
1344
  cropToBase64() {
1339
- const result = this.cropService.crop(this.state, 'base64');
1345
+ const result = this.cropService.crop(this.state.toCropInput(), 'base64');
1340
1346
  if (result) {
1341
1347
  this.imageCropped.emit(result);
1342
1348
  return result;