File
Implements
Metadata
selector |
lib-crop-image |
styleUrls |
./crop-image.component.scss |
templateUrl |
./crop-image.component.html |
Methods
Private
getUserMedia
|
getUserMedia()
|
|
Returns : Observable<UserMedia>
|
moveTo
|
moveTo(action: "up" | "down" | "right" | "left")
|
|
Parameters :
Name |
Type |
Optional |
action |
"up" | "down" | "right" | "left"
|
No
|
|
ngAfterViewInit
|
ngAfterViewInit()
|
|
|
ngOnDestroy
|
ngOnDestroy()
|
|
|
Private
readFile
|
readFile(file: any)
|
|
Parameters :
Name |
Type |
Optional |
file |
any
|
No
|
|
Private
validateFiles
|
validateFiles()
|
|
|
Private
_cropper
|
Type : Cropper
|
|
Private
_imageElement
|
Type : ElementRef
|
Decorators :
@ViewChild('image', {static: true})
|
|
Private
Readonly
_result
|
Type : UserMedia[]
|
|
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>
.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 with directive