Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce costly operations, improve app flow and respond to all inputs on runtime #602

86 changes: 56 additions & 30 deletions projects/demo-app/src/app/app.component.html
Original file line number Diff line number Diff line change
@@ -1,59 +1,85 @@
<input type="file" (change)="fileChangeEvent($event)" accept="image/*" />
<br />
<br />
<input [(ngModel)]="imageURL" placeholder="URL to load image" type="text" />
<input type="file" (change)="fileChangeEvent($event)" accept="image/*" />
<br />
<br />
<button (click)="toggleAspectRatio()">Aspect ratio: {{aspectRatio === 4/3 ? '4/3' : '16/5'}}</button>
<button (click)="containWithinAspectRatio = !containWithinAspectRatio;">{{containWithinAspectRatio ? 'Fill Aspect Ratio' : 'Contain Within Aspect Ratio'}}</button>
<button (click)="rotateLeft()">Rotate left</button>
<button (click)="rotateRight()">Rotate right</button>
<button (click)="flipHorizontal()">Flip horizontal</button>
<button (click)="flipVertical()">Flip vertical</button>
<br />
<br />
<button (click)="toggleContainWithinAspectRatio()">{{containWithinAspectRatio?'Fill Aspect Ratio':'Contain Within Aspect Ratio'}}</button>
<button (click)="toggleAspectRatio()">Aspect ratio: {{aspectRatio === 4/3 ? '4/3' : '16/5'}}</button>
<button (click)="resetImage()">Reset image</button>
<section>
<button (click)="maintainAspectRatio = !maintainAspectRatio;">{{maintainAspectRatio ? "Don't Maintain Aspect Ratio" : 'Maintain Aspect Ratio'}}</button>
<label for="cropperStaticWidth">Cropper Static Width:</label>
<input id="cropperStaticWidth" type="number" (keyup)="debounce($event)" />
<label for="cropperMinWidth">Cropper Min Width:</label>
<input id="cropperMinWidth" type="number" (keyup)="debounce($event)"/>
<label for="cropperMaxWidth">Cropper Max Width:</label>
<input id="cropperMaxWidth" type="number" (keyup)="debounce($event)"/>
<span></span>
<label for="cropperStaticHeight">Cropper Static Height:</label>
<input id="cropperStaticHeight" type="number" (keyup)="debounce($event)" />
<label for="cropperMinHeight">Cropper Min Height:</label>
<input id="cropperMinHeight" type="number" (keyup)="debounce($event)"/>
<label for="cropperMaxHeight">Cropper Max Height:</label>
<input id="cropperMaxHeight" type="number" (keyup)="debounce($event)"/>
</section>
<br />
<br />
<input [(ngModel)]="rotation" placeholder="Rotation" type="number" (ngModelChange)="updateRotation()" /> <button (click)="zoomOut()">Zoom -</button> <button (click)="zoomIn()">Zoom +</button>
<br />
<br/>
<input [(ngModel)]="transform.rotate" placeholder="Rotation" type="number" (ngModelChange)="updateRotation()" />
<button (click)="zoomOut()">Zoom -</button>
<button (click)="zoomIn()">Zoom +</button>
<button (click)="moveLeft()">move left</button>
<button (click)="moveRight()">move right</button>

<button (click)="moveTop()">move top</button>
<button (click)="moveBottom()">move bottom</button>
<button (click)="moveUp()">move up</button>
<button (click)="moveDown()">move down</button>
<button (click)="allowMoveImage = !allowMoveImage;">{{allowMoveImage ? 'Disable' : 'Enable'}} image panning</button>
<br/>
<br/>
<button (click)="allowMoveImage = !allowMoveImage;">{{allowMoveImage ? 'Disable' : 'Enable' }} image panning</button>
<button (click)="hidden = !hidden;">{{hidden ? 'Show' : 'Hide' }}</button>
<button (click)="hidden = !hidden;">{{hidden ? 'Show' : 'Hide'}}</button>
<button (click)="disabled = !disabled;">{{disabled ? 'Enable' : 'Disable'}} Cropper</button>
<button (click)="hideResizeSquares = !hideResizeSquares;">{{hideResizeSquares ? 'Show' : 'Hide'}} Resize Squares</button>
<button (click)="resetCropOnAspectRatioChange = !resetCropOnAspectRatioChange;">{{resetCropOnAspectRatioChange ? "Don't" : ''}} Reset Crop On Aspect Ratio Change</button>
<button (click)="toggleBackgroundColor()">Background Color: {{backgroundColor === 'red' ? 'blue' : 'red'}}</button>
<button (click)="test()">Random test</button>
<button (click)="resetImage()">Reset image</button>
<br/>
<br/>


<div class="cropper-wrapper">
<image-cropper
<div [style.display]="showCropper ? null : 'none'" class="cropper-wrapper">
<image-cropper
[imageChangedEvent]="imageChangedEvent"
[imageURL]="imageURL"
[maintainAspectRatio]="true"
[containWithinAspectRatio]="containWithinAspectRatio"
[cropperMinWidth]="128"
[aspectRatio]="aspectRatio"
[onlyScaleDown]="true"
[roundCropper]="false"
[canvasRotation]="canvasRotation"
[(transform)]="transform"
[alignImage]="'center'"
[style.display]="showCropper ? null : 'none'"
[allowMoveImage]="allowMoveImage"
[hidden]="hidden"
[disabled]="disabled"
[alignImage]="alignImage"
[roundCropper]="roundCropper"
[backgroundColor]="backgroundColor"
imageAltText="Alternative image text"
backgroundColor="red"
[allowMoveImage]="allowMoveImage"
[hideResizeSquares]="hideResizeSquares"
[canvasRotation]="canvasRotation"
[aspectRatio]="aspectRatio"
[containWithinAspectRatio]="containWithinAspectRatio"
[maintainAspectRatio]="maintainAspectRatio"
[cropperStaticWidth]="cropperStaticWidth"
[cropperStaticHeight]="cropperStaticHeight"
[cropperMinWidth]="cropperMinWidth"
[cropperMinHeight]="cropperMinHeight"
[cropperMaxWidth]="cropperMaxWidth"
[cropperMaxHeight]="cropperMaxHeight"
[resetCropOnAspectRatioChange]="resetCropOnAspectRatioChange"
[cropper]="cropper"
[(transform)]="transform"
[onlyScaleDown]="true"
output="blob"
format="png"
(imageCropped)="imageCropped($event)"
(imageLoaded)="imageLoaded()"
(cropperReady)="cropperReady($event)"
(loadImageFailed)="loadImageFailed()"
(transformChange)="transformChange($event)"
></image-cropper>
<div *ngIf="loading" class="loader">Loading...</div>
</div>
Expand Down
36 changes: 36 additions & 0 deletions projects/demo-app/src/app/app.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,39 @@ image-cropper {
font-size: 20px;
color: white;
}

input[type=number]{
width: 65px;
}

section {
border: 0;
display: grid;
justify-content: start;
align-items: center;
grid-template-columns: auto repeat(3, auto auto);
grid-template-rows: auto;
grid-gap: 0 5px;

label {
font-family: Arial, Helvetica, sans-serif;
font-size: 14px;
}

input {
height: -webkit-fill-available;
margin-right: 5px;
-moz-appearance: textfield;

&::-webkit-outer-spin-button,
&::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
}

button {
height: fit-content;
margin-right: 7px;
}
}
122 changes: 87 additions & 35 deletions projects/demo-app/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Component } from '@angular/core';
import { Dimensions, ImageCroppedEvent, ImageTransform, ImageCropperComponent } from 'ngx-image-cropper';
import { CropperPosition, Dimensions, ImageCroppedEvent, ImageTransform, ImageCropperComponent } from 'ngx-image-cropper';
import {DomSanitizer, SafeUrl} from '@angular/platform-browser';
import { NgIf } from '@angular/common';
import { FormsModule } from "@angular/forms";
Expand All @@ -12,23 +12,43 @@ import { FormsModule } from "@angular/forms";
imports: [NgIf, FormsModule, ImageCropperComponent]
})
export class AppComponent {
imageChangedEvent: Event | null = null;
showCropper = false;
loading = false;
croppedImage: SafeUrl = '';

imageChangedEvent: Event | null = null;
imageURL?: string;
hidden = false;
disabled = false;
alignImage = 'center' as const;
roundCropper = false;
backgroundColor = 'red';
allowMoveImage = false;
hideResizeSquares = false;
canvasRotation = 0;
rotation?: number;
translateH = 0;
translateV = 0;
scale = 1;
aspectRatio = 4 / 3;
showCropper = false;
containWithinAspectRatio = false;
maintainAspectRatio = false;
cropperStaticWidth = 0;
cropperStaticHeight = 0;
cropperMinWidth = 0;
cropperMinHeight = 0;
cropperMaxWidth = 0;
cropperMaxHeight = 0;
resetCropOnAspectRatioChange = true;
cropper: CropperPosition = { x1: 0, y1: 0, x2:0, y2:0 };
transform: ImageTransform = {
translateUnit: 'px'
translateUnit: 'px',
scale: 1,
rotate: 0,
flipH: false,
flipV: false,
translateH: 0,
translateV: 0
};
imageURL?: string;
loading = false;
allowMoveImage = false;
hidden = false;

timeout: any;
eventList = {};

constructor(
private sanitizer: DomSanitizer
Expand All @@ -42,7 +62,7 @@ export class AppComponent {

imageCropped(event: ImageCroppedEvent) {
this.croppedImage = this.sanitizer.bypassSecurityTrustUrl(event.objectUrl || event.base64 || '');
console.log(event);
console.log('CROPPED', event);
}

imageLoaded() {
Expand All @@ -59,6 +79,10 @@ export class AppComponent {
console.error('Load image failed');
}

transformChange(transform: ImageTransform){
console.log('transform changed', transform)
}

rotateLeft() {
this.loading = true;
setTimeout(() => { // Use timeout because rotating image is a heavy operation and will block the ui thread
Expand All @@ -78,28 +102,28 @@ export class AppComponent {
moveLeft() {
this.transform = {
...this.transform,
translateH: ++this.translateH
translateH: --this.transform.translateH!
};
}

moveRight() {
this.transform = {
...this.transform,
translateH: --this.translateH
translateH: ++this.transform.translateH!
};
}

moveTop() {
moveDown() {
this.transform = {
...this.transform,
translateV: ++this.translateV
translateV: ++this.transform.translateV!
};
}

moveBottom() {
moveUp() {
this.transform = {
...this.transform,
translateV: --this.translateV
translateV: --this.transform.translateV!
};
}

Expand All @@ -109,10 +133,10 @@ export class AppComponent {
this.transform = {
...this.transform,
flipH: flippedV,
flipV: flippedH
flipV: flippedH,
translateH: 0,
translateV: 0
};
this.translateH = 0;
this.translateV = 0;
}

flipHorizontal() {
Expand All @@ -130,42 +154,70 @@ export class AppComponent {
}

resetImage() {
this.scale = 1;
this.rotation = 0;
this.canvasRotation = 0;
this.cropper = {x1: 0, y1: 0, x2: 0, y2: 0};
this.maintainAspectRatio = false;
this.transform = {
translateUnit: 'px'
translateUnit: 'px',
scale: 1,
rotate: 0,
flipH: false,
flipV: false,
translateH: 0,
translateV: 0
};
}

zoomOut() {
this.scale -= .1;
this.transform = {
...this.transform,
scale: this.scale
scale: this.transform.scale! - .1
};
}

zoomIn() {
this.scale += .1;
this.transform = {
...this.transform,
scale: this.scale
scale: this.transform.scale! + .1
};
}

toggleContainWithinAspectRatio() {
this.containWithinAspectRatio = !this.containWithinAspectRatio;
}

updateRotation() {
this.transform = {
...this.transform,
rotate: this.rotation
};
}

toggleAspectRatio() {
this.aspectRatio = this.aspectRatio === 4 / 3 ? 16 / 5 : 4 / 3;
}
}

toggleBackgroundColor() {
this.backgroundColor = this.backgroundColor === 'red' ? 'blue' : 'red';
}

// prevent over triggering app when typing
debounce(event: any) {
clearTimeout(this.timeout);
(this.eventList as any)[event.target!.id] = event.target.value;
this.timeout = setTimeout(() => {
for (const [key, value] of Object.entries(this.eventList)) {
(this as any)[key] = Number(value);
}
this.eventList = {};
}, 500);
}

/*
Random Test button triggers this method
use it to test whatever you want
*/
test() {
this.canvasRotation = 3;
this.transform = {
...this.transform,
scale: 2
}
this.cropper = { x1: 190, y1: 221.5, x2: 583, y2: 344.3125 } // has 16/5 aspect ratio
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
class="ngx-ic-move"
role="presentation">
</div>
<ng-container *ngIf="!hideResizeSquares">
<ng-container *ngIf="!hideResizeSquares && !(cropperStaticWidth && cropperStaticHeight)">
<span class="ngx-ic-resize ngx-ic-topleft"
role="presentation"
(mousedown)="startMove($event, moveTypes.Resize, 'topleft')"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@
}
}

&.ngx-ix-hidden {
&.ngx-ic-hidden {
display: none;
}
}
Loading
Loading