Skip to content

Commit

Permalink
Merge pull request #252 from Mawi137/feature/zoom-rotate
Browse files Browse the repository at this point in the history
Feature/zoom rotate
  • Loading branch information
Mawi137 authored Jan 20, 2020
2 parents 90b7ef3 + d9f5885 commit e6d0e75
Show file tree
Hide file tree
Showing 16 changed files with 201 additions and 193 deletions.
50 changes: 9 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ Add the element to your HTML:
[imageChangedEvent]="imageChangedEvent"
[maintainAspectRatio]="true"
[aspectRatio]="4 / 3"
[resizeToWidth]="128"
format="png"
(imageCropped)="imageCropped($event)"
(imageLoaded)="imageLoaded()"
Expand Down Expand Up @@ -88,7 +87,6 @@ All inputs are optional. Either the `imageChangedEvent`, `imageBase64` or `image
| `imageFile` | Blob(File)| | The file you want to change (set to `null` to reset the cropper) |
| `imageBase64` | string | | If you don't want to use a file input, you can set a base64 image directly and it will be loaded into the cropper |
| `format` | string | png | Output format (png, jpeg, webp, bmp, ico) (not all browsers support all types, png is always supported, others are optional) |
| `outputType` | string | base64 | Output type ('base64', 'file' or 'both'). Converting the image to a Blob can be quite a heavy operation. With this option, you could choose to only get the base64 which will improve the speed of cropping significantly |
| `aspectRatio` | number | 1 / 1 | The width / height ratio (e.g. 1 / 1 for a square, 4 / 3, 16 / 9 ...) |
| `maintainAspectRatio` | boolean | true | Keep width and height of cropped image equal according to the aspectRatio |
| `containWithinAspectRatio` | boolean | false | When set to true, padding will be added around the image to make it fit to the aspect ratio |
Expand All @@ -105,13 +103,15 @@ All inputs are optional. Either the `imageChangedEvent`, `imageBase64` or `image
| `alignImage` | 'left' or 'center' | 'center' | Use this to align the image in the cropper either to the left or center. |
| `backgroundColor` | string | | Use this to set a backgroundColor, this is useful if you upload an image of a format with transparent colors and convert it to 'jpeg' or 'bmp'. The transparent pixels will then become the set color or the default value. Enter a color HashCode or one of known HTML color names (https://www.w3schools.com/tags/ref_colornames.asp).|
| `disabled` | boolean | false | Disables the component and prevents changing the cropper position |
| `canvasRotation` | number | 0 | Rotate the canvas (1 = 90deg, 2 = 180deg...) |
| `transform` | ImageTransform | {} | Flip, rotate and scale image |

### Outputs
| Name | Type | Description |
| ----------------------- | ----------------- | ----------- |
| `imageCropped` | ImageCroppedEvent | Emits an ImageCroppedEvent each time the image is cropped |
| `imageLoaded` | void | Emits when the image was loaded into the cropper |
| `cropperReady` | void | Emits when the cropper is ready to be interacted |
| `cropperReady` | Dimensions | Emits when the cropper is ready to be interacted. The Dimensions object that is returned contains the displayed image size |
| `startCropImage` | void | Emits when the component started cropping the image |
| `loadImageFailed` | void | Emits when a wrong file type was selected (only png, gif and jpg are allowed) |

Expand All @@ -120,12 +120,7 @@ To gain access to the image cropper's methods use `@ViewChild(ImageCropperCompon

| Name | Returns | Description |
| ----------------------- | ----------------- | ----------- |
| `rotateLeft` | void | Rotates the image to the left |
| `rotateRight` | void | Rotates the image to the right |
| `flipHorizontal` | void | Flips the image horizontally |
| `flipVertical` | void | Flips the image vertically |
| `resetImage` | void | Reset the image to the original |
| `crop` | ImageCroppedEvent (when `outputType` is `base64`) or Promise<ImageCroppedEvent> (when `outputType` is `file` or `both`)) | Crops the source image to the current cropper position. Accepts an output type as an argument, default is the one given in the `outputType` input (`base64`, `file` or `both`). Be sure to set `autoCrop` to `false` if you only wish to use this function directly. |
| `crop` | ImageCroppedEvent | Crops the source image to the current cropper position. Be sure to set `autoCrop` to `false` if you only wish to use this function directly. |

### Interfaces
#### CropperPosition
Expand All @@ -136,47 +131,20 @@ To gain access to the image cropper's methods use `@ViewChild(ImageCropperCompon
| x2 | number | X position of second coordinate (in px) |
| y2 | number | Y position of second coordinate (in px) |

#### Transformations
#### ImageTransform
| Property | Type | Description |
| -------- | ------- | ----------- |
| rotation | number | 0=No Rotation, 1=90°, 2=180°, 3=270° |
| flipH | boolean | Flipped Horizontally |
| flipV | boolean | Flipped Vertically |
| scale | number | Scale image (1=normal, 2=2x zoom...) |
| rotate | number | Rotation in degrees |
| flipH | boolean | Flipped horizontally |
| flipV | boolean | Flipped vertically |

#### ImageCroppedEvent
| Property | Type | Description |
| -------------------- | ------ | ----------- |
| base64 | string | Base64 string of the cropped image |
| file | Blob(File) | Blob of the cropped image |
| width | number | Width of the cropped image |
| height | number | Height of the cropped image |
| cropperPosition | CropperPosition | Position of the cropper when it was cropped relative to the displayed image size |
| imagePosition | CropperPosition | Position of the cropper when it was cropped relative to the original image size |
| offsetImagePosition | CropperPosition | Position of the cropper when it was cropped relative to the original image size without padding when containWithinAspectRatio is true |
| transform | Transformations | Rotation and Flip State |


### Polyfill for IE and Edge
If you wish to use the file output, you'll need to polyfill the `toBlob` method of the HTML Canvas for IE and Edge.
```
if (!HTMLCanvasElement.prototype.toBlob) {
Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
value: function (callback, type, quality) {
var dataURL = this.toDataURL(type, quality).split(',')[1];
setTimeout(function() {
var binStr = atob( dataURL ),
len = binStr.length,
arr = new Uint8Array(len);
for (var i = 0; i < len; i++ ) {
arr[i] = binStr.charCodeAt(i);
}
callback( new Blob( [arr], {type: type || 'image/png'} ) );
});
}
});
}
```
14 changes: 10 additions & 4 deletions projects/demo-app/src/app/app.component.html
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
<input type="file" (change)="fileChangeEvent($event)" />
<br />
<br />
<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)="resetImage()">Reset image</button>
<br />
<br />
<input [(ngModel)]="rotation" placeholder="Rotation" type="number" (keyup)="updateRotation()" /> <button (click)="zoomOut()">Zoom -</button> <button (click)="zoomIn()">Zoom +</button>

<div>
<image-cropper
Expand All @@ -18,14 +23,15 @@
[cropperMinWidth]="128"
[onlyScaleDown]="true"
[roundCropper]="false"
[canvasRotation]="canvasRotation"
[transform]="transform"
[alignImage]="'left'"
[style.display]="showCropper ? null : 'none'"
format="png"
outputType="base64"
(imageCropped)="imageCropped($event)"
(imageLoaded)="imageLoaded()"
(cropperReady)="cropperReady()"
(cropperReady)="cropperReady($event)"
(loadImageFailed)="loadImageFailed()"
[style.display]="showCropper ? null : 'none'"
[alignImage]="'left'"
></image-cropper>
</div>
<img [src]="croppedImage" [style.border]="croppedImage ? '1px solid black' : 'none'" />
70 changes: 60 additions & 10 deletions projects/demo-app/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Component, ViewChild } from '@angular/core';
import { ImageCroppedEvent, ImageCropperComponent } from 'ngx-image-cropper';
import { Dimensions, ImageCroppedEvent, ImageCropperComponent, ImageTransform } from 'ngx-image-cropper';
import { base64ToFile } from 'ngx-image-cropper';

@Component({
selector: 'app-root',
Expand All @@ -9,8 +10,12 @@ import { ImageCroppedEvent, ImageCropperComponent } from 'ngx-image-cropper';
export class AppComponent {
imageChangedEvent: any = '';
croppedImage: any = '';
canvasRotation = 0;
rotation = 0;
scale = 1;
showCropper = false;
containWithinAspectRatio = false;
transform: ImageTransform = {};

@ViewChild(ImageCropperComponent, {static: true}) imageCropper: ImageCropperComponent;

Expand All @@ -20,43 +25,88 @@ export class AppComponent {

imageCropped(event: ImageCroppedEvent) {
this.croppedImage = event.base64;
console.log(event);
console.log(event, base64ToFile(event.base64));
}

imageLoaded() {
this.showCropper = true;
console.log('Image loaded');
}

cropperReady() {
console.log('Cropper ready');
cropperReady(sourceImageDimensions: Dimensions) {
console.log('Cropper ready', sourceImageDimensions);
}

loadImageFailed() {
console.log('Load failed');
}

rotateLeft() {
this.imageCropper.rotateLeft();
this.canvasRotation--;
this.flipAfterRotate();
}

rotateRight() {
this.imageCropper.rotateRight();
this.canvasRotation++;
this.flipAfterRotate();
}

private flipAfterRotate() {
const flippedH = this.transform.flipH;
const flippedV = this.transform.flipV;
this.transform = {
...this.transform,
flipH: flippedV,
flipV: flippedH
};
}


flipHorizontal() {
this.imageCropper.flipHorizontal();
this.transform = {
...this.transform,
flipH: !this.transform.flipH
};
}

flipVertical() {
this.imageCropper.flipVertical();
this.transform = {
...this.transform,
flipV: !this.transform.flipV
};
}

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

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

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

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

updateRotation() {
this.transform = {
...this.transform,
rotate: this.rotation
};
}
}
5 changes: 4 additions & 1 deletion projects/demo-app/src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ImageCropperModule } from 'ngx-image-cropper';

import { AppComponent } from './app.component';
Expand All @@ -10,6 +11,8 @@ import { AppComponent } from './app.component';
],
imports: [
BrowserModule,
FormsModule,
ReactiveFormsModule,
ImageCropperModule
],
providers: [],
Expand Down
4 changes: 3 additions & 1 deletion projects/ngx-image-cropper/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ngx-image-cropper",
"version": "2.1.1",
"version": "3.0.0",
"description": "An image cropper for Angular",
"author": "Martijn Willekens",
"repository": {
Expand All @@ -16,6 +16,8 @@
"angular 6",
"angular 7",
"angular 8",
"ionic",
"ionic-angular",
"cropper",
"image cropper"
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@
*ngIf="safeImgDataUrl"
[src]="safeImgDataUrl"
[style.visibility]="imageVisible ? 'visible' : 'hidden'"
[style.transform]="safeTransformStyle"
(load)="imageLoadedInView()"
/>
<div
class="overlay"
[style.width.px]="maxSize.width"
[style.height.px]="maxSize.height"
></div>
<div class="cropper"
*ngIf="imageVisible"
[class.rounded]="roundCropper"
Expand All @@ -16,8 +22,8 @@
[style.height.px]="cropper.y2 - cropper.y1"
[style.margin-left]="alignImage === 'center' ? marginLeft : null"
[style.visibility]="imageVisible ? 'visible' : 'hidden'"
tabindex="0"
(keydown)="keyboardAccess($event)"
tabindex="0"
>
<div
(mousedown)="startMove($event, 'move')"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,21 @@
img.source-image {
max-width: 100%;
max-height: 100%;
transform-origin: center;
}
}

.overlay {
position: absolute;
pointer-events: none;
touch-action: none;
outline-width: 100vw;
outline-style: solid;
outline-color: white;
top: 0;
left: 0;
}

.cropper {
position: absolute;
display: flex;
Expand Down
Loading

0 comments on commit e6d0e75

Please sign in to comment.