Camilo Casadiego
Camilo Casadiego

Reputation: 918

Angular performance issues when cropping image

I'm using this component to crop images as part of an ionic/angular8 project, but I'm getting serious performance issues making the app irresponsive at some times, this is only manifesting when running from mobile, the desktop version doesn't show any issues, the crop works just fine.

Doing a performance review in some cases I see these garbage collections; I came from a java world, and GC is always a performance issue, but haven't found much about it, but I don't think its the root of the problem:

enter image description here

Here is another sample, seems to be a delay between the user interacting with the screen, and my inner method getting called, as you can see there's a FunctionCall, but which function.

enter image description here

My general code is almost the same as in the site but I'll share it here:

Html:

<div class="popup-form-container ion-text-center ion-padding">
<image-cropper
      [imageChangedEvent]="imageChangedEvent"
      [maintainAspectRatio]="true"
      [aspectRatio]="4 / 4"
      format="png"
      (imageCropped)="imageCropped($event)"
      (imageLoaded)="imageLoaded()"
      (cropperReady)="cropperReady()"
      (loadImageFailed)="loadImageFailed()"
  ></image-cropper>
  <ion-button fill="clear" color="danger" (click)="close()">Cancelar</ion-button>
  <ion-button color="green" (click)="save()">Guardar</ion-button>
</div>

And here you can see my class, I added a bunch of console logs in the process of finding the issue, and something I've seen is that when the app freezes and stays in the long call, there's no outpu, then out of the souden, lots in a row:

    @Component({
  selector: 'app-crop-image-popover',
  templateUrl: './crop-image-popover.component.html',
  styleUrls: ['./crop-image-popover.component.scss',
              '../../text-area/text-edit-popover/text-edit-popover.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class CropImagePopoverComponent implements OnInit {

  @Input()
  imageChangedEvent: any;

  private croppedImage: any = '';
  private uploader: FileUploader;
  private imageUrl: any;
  private errors: any;

  constructor(private modalCtrl: ModalController,
              private commonSrv: CommonService,
              protected cloudinary: Cloudinary,
              private alertCtrl: AlertController,
              private sysParamsSrv: SystemParamsService,
              private base64: Base64,
              private domSanitizer: DomSanitizer,
              private imageCompress: NgxImageCompressService,
              private changeDetector: ChangeDetectorRef) {
    // Inicializamos el compoente de carga de archivos

  }

  ngOnInit() {}

  imageCropped(event: ImageCroppedEvent) {
    console.log('cropped');
    this.croppedImage = event.base64;
  }
  imageLoaded() {
    console.log('loaded');
    // show cropper
//     this.commonSrv.hideSpinner();
  }
  cropperReady() {
    // cropper ready
    console.log('ready');
    this.commonSrv.hideSpinner();
  }
  loadImageFailed() {
    // show message
    console.log('failed');
  }

  close() {
    this.modalCtrl.dismiss();
  }
}

I've already tried updating the changeDetection strategy, removed some function calls I had in other pages, and most of the performance recommendations, but I don't seem to find the reason for my current problem

Upvotes: 3

Views: 1574

Answers (1)

nephiw
nephiw

Reputation: 2046

ngx-image-cropper, when you change the crop size/position it actually crops the image and is very resource intensive. If you can get away with it, you can only do the crop once by setting autoCrop to false like so:

<image-cropper
    [imageChangedEvent]="imageChangedEvent"
    [maintainAspectRatio]="true"
    [aspectRatio]="1"
    [autoCrop]="false"
    format="png"
    (imageLoaded)="imageLoaded()"
    (cropperReady)="cropperReady()"
    (loadImageFailed)="loadImageFailed()"
></image-cropper>
<ion-button fill="clear" color="danger" (click)="close()">Cancelar</ion-button>
<ion-button color="green" (click)="save()">Guardar</ion-button>

Then use the @ViewChild to get access to the cropper and thus the cropping methods. Here is what that looks like within the typescript file:

@ViewChild(ImageCropperComponent) imageCropper: ImageCropperComponent;

Then, when the user selects to keep the crop, you will need to do something like this:

function save(): void {
  const event = this.imageCropper.crop();
  this.croppedImage = event.base64;
}

This will fix some performance issues. But do note that it appears that iDevices struggle with handling cropping in the way that ngx-image-cropper does it. Specifically, if the user uses the camera while uploading an image, the image may require too much memory to convert to a base64 string in the Safari thread - as a result, iDevices can freeze when used normally. Here is one place where this is discussed: https://github.com/Mawi137/ngx-image-cropper/issues/481

Upvotes: 1

Related Questions