File

projects/maplander/shared/src/lib/components/crop-image/crop-image.component.ts

Implements

AfterViewInit OnDestroy

Metadata

selector lib-crop-image
styleUrls ./crop-image.component.scss
templateUrl ./crop-image.component.html

Index

Properties
Methods

Constructor

constructor(_data: DataCropImage, _dialogRef: MatDialogRef, _ngZone: NgZone)
Parameters :
Name Type Optional
_data DataCropImage No
_dialogRef MatDialogRef<CropImageComponent | UserMedia[]> No
_ngZone NgZone No

Methods

cancelCrop
cancelCrop()
Returns : void
finishCrop
finishCrop()
Returns : void
Private getUserMedia
getUserMedia()
Returns : Observable<UserMedia>
Private init
init()
Returns : void
moveTo
moveTo(action: "up" | "down" | "right" | "left")
Parameters :
Name Type Optional
action "up" | "down" | "right" | "left" No
Returns : void
ngAfterViewInit
ngAfterViewInit()
Returns : void
ngOnDestroy
ngOnDestroy()
Returns : void
Private readFile
readFile(file: any)
Parameters :
Name Type Optional
file any No
Returns : void
Private validateFiles
validateFiles()
Returns : void
zoom
zoom(out: boolean)
Parameters :
Name Type Optional
out boolean No
Returns : void

Properties

Private _cropper
Type : Cropper
Private _imageElement
Type : ElementRef
Decorators :
@ViewChild('image', {static: true})
Private Readonly _result
Type : UserMedia[]
Public circle
Type : boolean
Public load
Type : boolean
Public src
Type : string
import {AfterViewInit, Component, ElementRef, Inject, NgZone, OnDestroy, ViewChild} from '@angular/core';
import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
import Cropper from 'cropperjs';
import {DataCropImage} from '../../../utils/models/data-crop-image';
import {UserMedia} from '@maplander/types';
import {Observable} from 'rxjs';

@Component({
  selector: 'lib-crop-image',
  templateUrl: './crop-image.component.html',
  styleUrls: ['./crop-image.component.scss']
})
export class CropImageComponent implements AfterViewInit, OnDestroy {
  @ViewChild('image', {static: true}) private _imageElement: ElementRef;
  private _cropper: Cropper;
  public load: boolean;
  public src: string;
  public circle: boolean;
  private readonly _result: UserMedia[];

  constructor(
    @Inject(MAT_DIALOG_DATA) private _data: DataCropImage,
    private _dialogRef: MatDialogRef<CropImageComponent, UserMedia[]>,
    private _ngZone: NgZone
  ) {
    this.circle = false;
    this._result = [];
    this.load = true;
  }

  ngAfterViewInit(): void {
    this.circle = this._data.circle || false;
    this.validateFiles();
  }

  ngOnDestroy(): void {
    this._cropper.destroy();
  }

  zoom(out: boolean): void {
    this._cropper.zoom(out ? -1 : 1);
  }

  moveTo(action: 'up' | 'down' | 'right' | 'left'): void {
    switch (action) {
      case 'left':
        this._cropper.move(-10, 0);
        break;
      case 'right':
        this._cropper.move(10, 0);
        break;
      case 'up':
        this._cropper.move(0, -10);
        break;
      case 'down':
        this._cropper.move(0, 10);
        break;
    }
  }

  finishCrop(): void {
    this.load = true;
    setTimeout(() => {
      this.getUserMedia().subscribe(response => {
        this._ngZone.run(() => {
          this._result.push(response);
          this.validateFiles();
        });
      });
    }, 200);
  }

  cancelCrop(): void {
    this._dialogRef.close(null);
  }

  private validateFiles(): void {
    if (!Array.isArray(this._data.files)) {
      return this._dialogRef.close(null);
    }
    if (this._data.files.length > 0) {
      const file = this._data.files.splice(0, 1);
      this.readFile(file[0]);
    } else {
      this._dialogRef.close(this._result);
    }
  }

  private readFile(file: any): void {
    const fr = new FileReader();
    fr.addEventListener('load', (e: any) => {
      this._imageElement.nativeElement.src = e.target.result;
      this.init();
    });
    fr.readAsDataURL(file);
  }

  private init(): void {
    if (!this._cropper) {
      this._cropper = new Cropper(this._imageElement.nativeElement, {
        zoomable: true,
        scalable: false,
        aspectRatio: this._data.aspectRatio || 4 / 4,
        ready: () => {
          this.load = false;
        }
      });
    } else {
      this._cropper.replace(this._imageElement.nativeElement.src);
    }
  }

  private getUserMedia(): Observable<UserMedia> {
    return new Observable<UserMedia>(observer => {
      const canvas = this._cropper.getCroppedCanvas();
      const base = canvas.toDataURL('image/png');
      canvas.toBlob((blob) => {
        if (blob) {
          observer.next(new UserMedia(blob, base));
        } else {
          observer.error(null);
        }
        observer.complete();
      });
    });
  }
}
<div class="crop-image">
  <h2 class="crop-image__title">
    Recortar imagen
  </h2>
  <mat-dialog-content class="crop-image__content">
    <div class="crop-image__loading" [ngStyle]="{'display': !load ? 'none' : 'flex'}">
      <mat-spinner [diameter]="45"></mat-spinner>
      <p>Cargando imagen...</p>
    </div>
    <div class="crop-image__img-to-crop" [ngClass]="{'radius': circle}">
      <img #image src="" class="img-to-crop__img" alt="Image to crop">
    </div>
    <div class="crop-image__crop-actions">
      <button mat-icon-button [disabled]="load" (click)="zoom(false)">
        <mat-icon>zoom_in</mat-icon>
      </button>
      <button mat-icon-button [disabled]="load" (click)="zoom(true)">
        <mat-icon>zoom_out</mat-icon>
      </button>
      <button mat-icon-button [disabled]="load" (click)="moveTo('left')">
        <mat-icon>arrow_back</mat-icon>
      </button>
      <button mat-icon-button [disabled]="load" (click)="moveTo('right')">
        <mat-icon>arrow_forward</mat-icon>
      </button>
      <button mat-icon-button [disabled]="load" (click)="moveTo('up')">
        <mat-icon>arrow_downward</mat-icon>
      </button>
      <button mat-icon-button [disabled]="load" (click)="moveTo('down')">
        <mat-icon>arrow_upward</mat-icon>
      </button>
    </div>
  </mat-dialog-content>
  <mat-dialog-actions class="crop-image__actions">

    <button mat-button [disabled]="load" (click)="cancelCrop()">CANCELAR</button>
    <button mat-button [disabled]="load" (click)="finishCrop()">ACEPTAR</button>
  </mat-dialog-actions>
</div>

./crop-image.component.scss


.radius {
  ::ng-deep .cropper-view-box {
    border-radius: 50% !important;
  }

  ::ng-deep .cropper-view-box {
    box-shadow: 0 0 0 2px #39f !important;
    outline: 0 !important;
  }
}

.crop-image__content {
  position: relative;
  min-width: 30vw;
  max-width: 30vw;
  @media screen and (max-width: 600px){
    max-width: 100vw;
  }
}

.crop-image__crop-actions {
  display: flex;
  padding: 8px 0;
  justify-content: space-around;
}

.crop-image__img-to-crop {
  width: 100%;
  height: 300px;
}
.img-to-crop__img {
  width: 100%;
}

.crop-image__loading {
  position: absolute;
  z-index: 10;
  width: 100%;
  height: 100%;
  background: rgba(255, 255, 255, .5);
  left: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.crop-image__actions {
  display: flex;
  justify-content: space-around;
}
Legend
Html element
Component
Html element with directive

result-matching ""

    No results matching ""