2

I am using the ngx-image-cropper library for cropping images in my Angular 8 project.

What I want to do is:

  • I want to show the cropper inside a modal on selecting a file.
  • The modal has a crop button on clicking which the image will be cropped (no autocrop) and a cancel button which can be used to close the modal without cropping the image.
  • After the image is cropped, the modal will be closed and there will be an edit button which can be used to recrop the image (On clicking crop, I essentially crop the image and hide the modal. So on clicking edit, I just show the hidden modal again.).
  • If another file is selected from the file input, the modal will be shown with the new image. The user can either crop or cancel. If it is cancelled, I want to reset the source image of the cropper to the previous image. How can I do that?

I have tried the following, but doesn't work:


    lastCropperPosition: CropperPosition;
    lastCroppedImage: any;

    crop() {
        this.imageCropper.crop();
        this.lastCropperPosition = this.getCurrentCropperPosition();
        this.lastCroppedImage = new ElementRef(
          this.imageCropper.sourceImage.nativeElement
        );
        this.logoModal.hide();
    }

    cancelCrop() {
        if (this.lastCroppedImage) {
          this.imageCropper.sourceImage = this.lastCroppedImage;
        }

        if (this.lastCropperPosition) {
          this.imageCropper.cropper = this.getLastCropperPosition();
        }

        this.logoModal.hide();
    }

    getCurrentCropperPosition() {
        return {
          x1: this.imageCropper.cropper.x1,
          x2: this.imageCropper.cropper.x2,
          y1: this.imageCropper.cropper.y1,
          y2: this.imageCropper.cropper.y2
        };
    }

    getLastCropperPosition() {
        return {
          x1: this.lastCropperPosition.x1,
          x2: this.lastCropperPosition.x2,
          y1: this.lastCropperPosition.y1,
          y2: this.lastCropperPosition.y2
        };
    }

HTML Code:

    <div bsModal #logoModal="bs-modal" class="modal fade" tabindex="-1"
      role="dialog" aria-labelledby="logoModalLabel" aria-hidden="true">
      <div class="modal-dialog" role="document">
        <div class="modal-content">
          <div class="modal-header">
            <h4 class="modal-title">Crop Image</h4>
          </div>
          <div class="modal-body">
            <image-cropper
              [imageChangedEvent]="imageChangedEvent"
              [maintainAspectRatio]="true"
              [aspectRatio]="rules.logo.maxWidth / rules.logo.maxHeight"
              [cropperMinWidth]="rules.logo.minWidth"
              [resizeToWidth]="rules.logo.maxWidth"
              [autoCrop]="false"
              onlyScaleDown="true"
              outputType="both"
              (imageCropped)="imageCropped($event)"
              (imageLoaded)="imageLoaded()"
              (cropperReady)="cropperReady()"
              (loadImageFailed)="loadImageFailed()"></image-cropper>
          </div>
          <div class="modal-footer">
            <button type="button" class="btn btn-sm btn-secondary"
              (click)="cancelCrop()">Cancel</button>
            <button type="button" class="btn btn-sm btn-primary"
              (click)="crop()">Crop</button>
          </div>
        </div>
      </div>
    </div>

Is using the sourceImage property the right way to do what I want to do? I change the this.lastCroppedImage only inside crop() method. But it seems to be changing whenever a new image is selected in the file input. Why does that happen?

PS: I have already tried assigning the sourceImage directly without creating a new ElementRef. That doesn't work either.

Stackblitz example: https://stackblitz.com/edit/image-cropper-r5pyqh

Steps to reproduce in Stackblitz:

Browse and crop an image.
Browse again and select new image.
Click cancel in the crop window.
Click recrop. Observe that the image in crop window is the second one. Instead I want the first one.
Nimish David Mathew
  • 2,958
  • 6
  • 29
  • 45

2 Answers2

1

You can create a variable to store a previous image and when you want to recrop, then just assign it to the src of an image. Let me show an example:

ImageCropperComponent.ts:

@Input() imgToBeSet: any;
@Input() showPrevImage: false;
@Output() cropperReady = new EventEmitter<any>();

ngOnChanges(changes: SimpleChanges): void {
    if (this.imgToBeSet && this.showPrevImage){
      console.log('***** set prev image');
      this.safeImgDataUrl = this.imgToBeSet;
    }  
}

AppComponent.ts:

currentImgUrl: any;
recropImage: false;

reCrop() {
    this.logoModal.show();
    this.recropImage = true;
}

cropperReady(eventArgs: any) {
    if (!this.currentImgUrl) {
        this.currentImgUrl = eventArgs.currentImgUrl;
        console.log(`currentImgUrl is`, this.currentImgUrl);
    }
}

AppComponent.html:

<div class="modal-body">
    <image-cropper
    #imageCropper
      [imageChangedEvent]="imageChangedEvent"
      [maintainAspectRatio]="true"
      [aspectRatio]="4 / 3"
      [autoCrop]="false"
      [imgToBeSet] = "currentImgUrl"
      [showPrevImage] = "recropImage"
      onlyScaleDown="true"
      outputType="both"
      (imageCropped)="imageCropped($event)"
      (imageLoaded)="imageLoaded()"
      (cropperReady)="cropperReady($event)"
      (loadImageFailed)="loadImageFailed()"></image-cropper>
  </div>

The complete example can be seen here at stackblitz.

StepUp
  • 36,391
  • 15
  • 88
  • 148
  • Thanks alot for the answer. Please note that I can't change the code in `ImageCropperComponent.ts` since it's part of the cropper package. But, I think the idea here is to store the `safeImgDataUrl` of the previous image. After restoring the url, the image in the cropper window changed to the previous image. But, if I crop the image, the crop does not apply on the previous image. It applies on the current image that I just cancelled. Also, when the url is restored, the `imageLoaded()` is fired which causes the cropper modal to open unnecessarily. Any way to solve these issues? – Nimish David Mathew Dec 18 '19 at 04:48
  • @NimishDavidMathew sorry, I have no time and have to do my own job. You can store cropped image in some service and check whether the service contains a previous image. Feel free to upvote or mark it an answer if you feel that it was helpful to you. – StepUp Dec 18 '19 at 06:59
0

Please use a function to reset it like:

   resetImage() {
        this.scale = 1;
        this.rotation = 0;
        this.canvasRotation = 0;
        this.transform = {};
    }

Here I added stackblitz so you get an idea.

Manish Patidar
  • 526
  • 4
  • 14