insight-ngx-image-editor
Version:
Angular 5 Image Editor
390 lines (378 loc) • 18.9 kB
JavaScript
import { Component, EventEmitter, Input, Output, ViewChild, ViewEncapsulation, NgModule } from '@angular/core';
import { FlexLayoutModule } from '@angular/flex-layout';
import { MatAutocompleteModule, MatButtonModule, MatButtonToggleModule, MatIconModule, MatInputModule, MatMenuModule, MatProgressSpinnerModule, MatSliderModule, MatDialogModule, MatTabsModule, MatTooltipModule } from '@angular/material';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
class NgxImageEditorComponent {
constructor() {
this.file = new EventEmitter();
this.zoomIn = 0;
this.sliderValue = 0;
this.loading = true;
this.canvasFillColor = '#fff';
this.state = new EditorOptions();
}
/**
* @param {?} config
* @return {?}
*/
set config(config) {
this.state = config;
}
/**
* @return {?}
*/
ngOnInit() {
this.handleStateConfig();
}
/**
* @return {?}
*/
ngOnDestroy() {
this.cropper.destroy();
}
/**
* @return {?}
*/
ngAfterViewInit() {
if (!this.state.File && this.state.ImageUrl) {
this.initializeCropper();
}
}
/**
* @return {?}
*/
handleStateConfig() {
this.state.ImageType = this.state.ImageType ? this.state.ImageType : 'image/jpeg';
if (this.state.ImageUrl) {
this.state.File = null;
this.previewImageURL = this.state.ImageUrl;
}
if (this.state.File) {
this.state.ImageUrl = null;
this.convertFileToBase64(this.state.File);
}
if (this.state.AspectRatios) {
this.addRatios(this.state.AspectRatios);
}
else {
this.ratios = NGX_DEFAULT_RATIOS;
}
if (!this.state.ImageUrl && !this.state.File) {
console.error("Property ImageUrl or File is missing, Please provide an url or file in the config options.");
}
if (!this.state.ImageName) {
console.error("Property ImageName is missing, Please provide a name for the image.");
}
}
/**
* @param {?} file
* @return {?}
*/
convertFileToBase64(file) {
const /** @type {?} */ reader = new FileReader();
reader.addEventListener("load", (e) => {
this.previewImageURL = e.target["result"];
}, false);
reader.readAsDataURL(file);
reader.onloadend = (() => {
this.initializeCropper();
});
}
/**
* @param {?} ratios
* @return {?}
*/
addRatios(ratios) {
this.ratios = [];
ratios.forEach((ratioType) => {
const /** @type {?} */ addedRation = NGX_DEFAULT_RATIOS.find((ratio) => {
return ratio.text === ratioType;
});
this.ratios.push(addedRation);
});
}
/**
* @return {?}
*/
handleCrop() {
this.loading = true;
this.croppedImage = this.cropper.getCroppedCanvas({ fillColor: this.canvasFillColor })
.toDataURL(this.state.ImageType);
this.imageWidth = this.croppedImg.nativeElement.width;
this.imageHeight = this.croppedImg.nativeElement.height;
this.cropper.getCroppedCanvas({ fillColor: this.canvasFillColor }).toBlob((blob) => {
this.blob = blob;
});
this.zoomIn = 1;
this.loading = false;
}
/**
* @return {?}
*/
undoCrop() {
this.croppedImage = null;
this.blob = null;
setTimeout(() => {
this.initializeCropper();
}, 100);
}
/**
* @return {?}
*/
saveImage() {
this.file.emit(new File([this.blob], this.state.ImageName, { type: this.state.ImageType }));
}
/**
* @return {?}
*/
initializeCropper() {
this.cropper = new Cropper(this.previewImage.nativeElement, {
zoomOnWheel: true,
viewMode: 0,
center: true,
ready: () => this.loading = false,
dragMode: 'move',
crop: (e) => {
this.imageHeight = Math.round(e.detail.height);
this.imageWidth = Math.round(e.detail.width);
this.cropBoxWidth = Math.round(this.cropper.getCropBoxData().width);
this.cropBoxHeight = Math.round(this.cropper.getCropBoxData().height);
this.canvasWidth = Math.round(this.cropper.getCanvasData().width);
this.canvasHeight = Math.round(this.cropper.getCanvasData().height);
}
});
this.setRatio(this.ratios[0].value);
}
/**
* @param {?} value
* @return {?}
*/
setRatio(value) {
this.cropper.setAspectRatio(value);
}
/**
* @param {?} input
* @param {?=} zoom
* @return {?}
*/
zoomChange(input, zoom) {
if (this.croppedImage) {
if (zoom) {
zoom === 'zoomIn' ? this.zoomIn += 0.1 : this.zoomIn -= 0.1;
}
else {
if (input < this.sliderValue) {
this.zoomIn = -Math.abs(input / 1000);
}
else {
this.zoomIn = Math.abs(input / 1000);
}
}
if (this.zoomIn <= 0.1) {
this.zoomIn = 0.1;
}
}
else {
if (zoom) {
this.cropper.zoom(input);
this.zoomIn = input;
}
else {
if (input < this.sliderValue) {
this.cropper.zoom(-Math.abs(input / 1000));
}
else {
this.cropper.zoom(Math.abs(input / 1000));
}
if (input === 0) {
this.cropper.zoom(-1);
}
}
}
if (!zoom) {
this.sliderValue = input;
}
else {
input > 0 ? this.sliderValue += Math.abs(input * 1000) : this.sliderValue -= Math.abs(input * 1000);
}
if (this.sliderValue < 0) {
this.sliderValue = 0;
}
}
/**
* @param {?} canvasWidth
* @return {?}
*/
setImageWidth(canvasWidth) {
if (canvasWidth) {
this.cropper.setCanvasData({
left: this.cropper.getCanvasData().left,
top: this.cropper.getCanvasData().top,
width: Math.round(canvasWidth),
height: this.cropper.getCanvasData().height
});
}
}
/**
* @param {?} canvasHeight
* @return {?}
*/
setImageHeight(canvasHeight) {
if (canvasHeight) {
this.cropper.setCanvasData({
left: this.cropper.getCanvasData().left,
top: this.cropper.getCanvasData().top,
width: this.cropper.getCanvasData().width,
height: Math.round(canvasHeight)
});
}
}
/**
* @param {?} cropBoxWidth
* @return {?}
*/
setCropBoxWidth(cropBoxWidth) {
if (cropBoxWidth) {
this.cropper.setCropBoxData({
left: this.cropper.getCropBoxData().left,
top: this.cropper.getCropBoxData().top,
width: Math.round(cropBoxWidth),
height: this.cropper.getCropBoxData().height
});
}
}
/**
* @param {?} cropBoxHeight
* @return {?}
*/
setCropBoxHeight(cropBoxHeight) {
if (cropBoxHeight) {
this.cropper.setCropBoxData({
left: this.cropper.getCropBoxData().left,
top: this.cropper.getCropBoxData().top,
width: this.cropper.getCropBoxData().width,
height: Math.round(cropBoxHeight)
});
}
}
/**
* @return {?}
*/
centerCanvas() {
const /** @type {?} */ cropBoxLeft = (this.cropper.getContainerData().width - this.cropper.getCropBoxData().width) / 2;
const /** @type {?} */ cropBoxTop = (this.cropper.getContainerData().height - this.cropper.getCropBoxData().height) / 2;
const /** @type {?} */ canvasLeft = (this.cropper.getContainerData().width - this.cropper.getCanvasData().width) / 2;
const /** @type {?} */ canvasTop = (this.cropper.getContainerData().height - this.cropper.getCanvasData().height) / 2;
this.cropper.setCropBoxData({
left: cropBoxLeft,
top: cropBoxTop,
width: this.cropper.getCropBoxData().width,
height: this.cropper.getCropBoxData().height
});
this.cropper.setCanvasData({
left: canvasLeft,
top: canvasTop,
width: this.cropper.getCanvasData().width,
height: this.cropper.getCanvasData().height
});
}
}
NgxImageEditorComponent.decorators = [
{ type: Component, args: [{
selector: 'ngx-image-editor',
template: "\n <div class=\"ngx-image-editor-component1\" fxLayout=\"column\" fxLayoutAlign=\"center stretch\">\n <div mat-dialog-title class=\"photo-editor-header\">\n <button [hidden]=\"croppedImage\" mat-icon-button color=\"accent\" matTooltip=\"Crop image\"\n (click)=\"handleCrop()\">\n <mat-icon>crop</mat-icon>\n </button>\n </div>\n\n <div mat-dialog-content\n #dialogCropContainer\n class=\"dialog-crop-container\"\n fxLayout=\"column\"\n fxLayoutAlign=\"center center\"\n fxFlex=\"grow\">\n <ng-template [ngIf]=\"!croppedImage\">\n <div\n [style.visibility]=\"loading ? 'hidden' : 'visible'\"\n [style.background]=\"canvasFillColor\"\n class=\"img-container\">\n <img #previewimg\n [src]=\"previewImageURL\">\n </div>\n </ng-template>\n <ng-template [ngIf]=\"croppedImage && !loading\">\n <div class=\"cropped-image\">\n <img #croppedImg\n [ngStyle]=\"{'transform': 'scale(' + zoomIn + ')'}\"\n [src]=\"croppedImage\">\n </div>\n </ng-template>\n <mat-progress-spinner *ngIf=\"loading\" mode=\"indeterminate\"></mat-progress-spinner>\n </div>\n\n <div\n class=\"dialog-button-actions\"\n mat-dialog-actions\n fxLayout=\"column\"\n align=\"start\"\n fxFlex=\"nogrow\">\n\n <div class=\"image-detail-toolbar\" fxFlex=\"100\">\n <div class=\"image-dimensions\"><b>Width:</b> <span>{{imageWidth}}px</span> <b>Height:</b> <span>{{imageHeight}}px</span></div>\n <span fxFlex></span>\n <div class=\"image-zoom\">\n <button mat-icon-button color=\"accent\" >\n <span>{{sliderValue}}%</span>\n </button>\n <mat-slider vertical [value]=\"sliderValue\" (input)=\"zoomChange($event.value)\" thumbLabel></mat-slider>\n <button mat-icon-button color=\"accent\" >\n zoom\n </button>\n </div>\n </div>\n <div fxLayout=\"row\" [style.visibility]=\"croppedImage ? 'hidden' : 'visible'\">\n <mat-button-toggle-group\n #dragMode=\"matButtonToggleGroup\"\n (change)=\"cropper.setDragMode($event.value)\"\n value=\"move\">\n <mat-button-toggle value=\"move\" matTooltip=\"Move mode\">\n <mat-icon>open_with</mat-icon>\n </mat-button-toggle>\n <mat-button-toggle value=\"crop\" matTooltip=\"Crop mode\">\n <mat-icon>crop</mat-icon>\n </mat-button-toggle>\n </mat-button-toggle-group>\n\n <mat-button-toggle-group\n #selectRatio=\"matButtonToggleGroup\"\n (change)=\"setRatio($event.value)\"\n value=\"{{ratios[0].value}}\">\n <mat-button-toggle *ngFor=\"let ratio of ratios\" value=\"{{ratio.value}}\" matTooltip=\"Aspect ratio\">\n {{ratio.text}}\n </mat-button-toggle>\n </mat-button-toggle-group>\n\n </div>\n </div>\n\n <div class=\"cropped-image-buttons\" [style.visibility]=\"!croppedImage ? 'hidden' : 'visible'\">\n <button mat-raised-button id=\"cropimage\" color=\"accent\" (click)=\"undoCrop()\">\n <span>Undo</span>\n </button>\n <button mat-raised-button id=\"saveimage\" color=\"accent\" (click)=\"saveImage()\">\n <span>Apply</span>\n </button>\n </div>\n </div>\n\n ",
styles: ["\n\n .ngx-image-editor-component .photo-editor-header {\n display: flex;\n justify-content: space-around;\n width: 7%;\n padding: 0;\n z-index: 100;\n margin: 0;\n top: 0;\n position: relative;\n }\n\n .ngx-image-editor-component .photo-editor-header > .mat-icon {\n padding: 0 10px;\n }\n\n .ngx-image-editor-component .photo-editor-header > .file-name {\n flex: 1 1 100%;\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n }\n\n .ngx-image-editor-component mat-progress-spinner {\n position: absolute;\n }\n\n .ngx-image-editor-component .dialog-crop-container {\n width: 800px;\n height: 400px;\n overflow: hidden;\n }\n\n .ngx-image-editor-component .cropper-bg {\n background-image: none !important;\n }\n\n .ngx-image-editor-component .cropper-bg > .cropper-modal {\n opacity: 1 !important;\n background: none;\n }\n\n .ngx-image-editor-component .img-container {\n width: 800px !important;\n height: 400px !important;\n }\n\n .ngx-image-editor-component .cropped-image img {\n width: auto !important;\n height: auto !important;\n max-width: 800px !important;\n max-height: 400px !important;\n }\n\n .ngx-image-editor-component .dialog-button-actions {\n position: relative;\n padding: 0;\n }\n\n .ngx-image-editor-component .dialog-button-actions:last-child {\n margin: 0;\n }\n\n .ngx-image-editor-component .dialog-button-actions > DIV mat-button-toggle-group {\n margin: 20px;\n }\n\n .ngx-image-editor-component .dialog-button-actions .cropped-image-buttons {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n }\n\n .ngx-image-editor-component .dialog-button-actions > .canvas-config {\n padding: 5px;\n margin: 0 20px;\n }\n\n \n\n .ngx-image-editor-component .dialog-button-actions > .canvas-config md2-colorpicker {\n width: 200px !important;\n }\n \n\n .ngx-image-editor-component .dialog-button-actions .image-detail-toolbar > .image-zoom {\n display: flex;\n align-items: center;\n padding: 0 10px;\n }\n \n .ngx-image-editor-component .dialog-button-actions .image-detail-toolbar > .image-zoom .mat-slider-vertical .mat-slider-wrapper .mat-slider-thumb-container {\n cursor: grab;\n }\n \n\n .ngx-image-editor-component .dialog-button-actions .image-detail-toolbar > .image-dimensions {\n padding: 0 10px;\n font-size: 14px;\n width: 200px;\n max-width: 200px;\n }\n\n \n\n\n\n\n\n\n\n\n\n\n "],
encapsulation: ViewEncapsulation.None
},] },
];
/** @nocollapse */
NgxImageEditorComponent.ctorParameters = () => [];
NgxImageEditorComponent.propDecorators = {
"previewImage": [{ type: ViewChild, args: ['previewimg',] },],
"croppedImg": [{ type: ViewChild, args: ['croppedImg',] },],
"config": [{ type: Input },],
"file": [{ type: Output },],
};
/**
* @record
*/
class EditorOptions {
}
/**
* @record
*/
const NGX_DEFAULT_RATIOS = [
{
value: 16 / 9, text: '16:9'
},
{
value: 4 / 3, text: '4:3'
},
{
value: 1 / 1, text: '1:1'
},
{
value: 2 / 3, text: '2:3'
},
{
value: 0 / 0, text: 'Default'
}
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
class NgxImageEditorModule {
/**
* @return {?}
*/
static forRoot() {
return {
ngModule: NgxImageEditorModule,
};
}
}
NgxImageEditorModule.decorators = [
{ type: NgModule, args: [{
imports: [
FormsModule,
BrowserAnimationsModule,
CommonModule,
ReactiveFormsModule,
FlexLayoutModule,
MatButtonModule,
MatIconModule,
MatDialogModule,
MatInputModule,
MatMenuModule,
MatProgressSpinnerModule,
MatTabsModule,
MatTooltipModule,
MatButtonToggleModule,
MatSliderModule,
MatAutocompleteModule
],
declarations: [
NgxImageEditorComponent
],
exports: [NgxImageEditorComponent]
},] },
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* Generated bundle index. Do not edit.
*/
export { NgxImageEditorModule, NgxImageEditorComponent, EditorOptions, NGX_DEFAULT_RATIOS };