When you are working with images inside your Ionic app and allow users to capture images, chances are high you might want to offer some sort of image modification or cropping to bring the images in perfect shape.
The good news is, that you can easily add different crop operations to your app using CropperJS and especially the Angular wrapper for the crop library.
Inside this Quick Win we will build an Ionic image cropping app that allows to capture an image and perform all kinds of modifications until you can export the image to a base64 string that you could use to upload the final image like you can see below!
Starting our Ionic Image Crop App
We start with a basic blank Ionic app and install the cropping library plus the Camera plugin to capture new images inside our app:
1 2 3 4 5 |
ionic start cropImage blank cd cropImage npm install angular-cropperjs@v0.1.5 ionic cordova plugin add cordova-plugin-camera npm install --save @ionic-native/camera |
Now make sure to import everything into your app/app.module.ts like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
import { BrowserModule } from '@angular/platform-browser'; import { ErrorHandler, NgModule } from '@angular/core'; import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular'; import { SplashScreen } from '@ionic-native/splash-screen'; import { StatusBar } from '@ionic-native/status-bar'; import { MyApp } from './app.component'; import { HomePage } from '../pages/home/home'; import { AngularCropperjsModule } from 'angular-cropperjs'; import { Camera } from '@ionic-native/camera'; @NgModule({ declarations: [ MyApp, HomePage ], imports: [ BrowserModule, IonicModule.forRoot(MyApp), AngularCropperjsModule ], bootstrap: [IonicApp], entryComponents: [ MyApp, HomePage ], providers: [ StatusBar, SplashScreen, {provide: ErrorHandler, useClass: IonicErrorHandler}, Camera ] }) export class AppModule {} |
Note: If you develop your app for iOS you need to include the right permissions for the camera or photo library, and the easiest way to automatically add them is by adding these lines to your config.xml:
1 2 3 4 5 6 |
<edit-config file="*-Info.plist" mode="merge" target="NSCameraUsageDescription"> <string>need camera access to take pictures</string> </edit-config> <edit-config file="*-Info.plist" mode="merge" target="NSPhotoLibraryUsageDescription"> <string>need photo library access to get pictures from there</string> </edit-config> |
Now the iOS project will be build with the additional entries to the plist and Apple won’t complain. Of course make sure to add useful information why you are using the plugins instead of the standard string!
Adding CropperJS Image cropping Functions
Now we get to the meat of this article, which is using the cropping functions. But actually, it’s so simple that most of the functions don’t really need explanations.
To keep a reference to the cropping area we use a viewchild and also specify some options for the cropping tool that will allow us to perform different modifications on the image.
The capturing process is the standard code for opening the camera and using the result as a base64 image string that will be assigned to our crop component inside the view.
All of the functions can then be called on the this.angularCropper.cropper
which is the cropper reference of our component. Finally, you can also export the cropped and edited image again as a base64 string that we will simply display, but you could use this result to send it to your server of course!
Go ahead and change your pages/home/home.ts to:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
import { Component, ViewChild } from '@angular/core'; import { NavController } from 'ionic-angular'; import { AngularCropperjsComponent } from 'angular-cropperjs'; import { CameraOptions, Camera } from '@ionic-native/camera'; @Component({ selector: 'page-home', templateUrl: 'home.html' }) export class HomePage { @ViewChild('angularCropper') public angularCropper: AngularCropperjsComponent; cropperOptions: any; croppedImage = null; myImage = null; scaleValX = 1; scaleValY = 1; constructor(public navCtrl: NavController, private camera: Camera) { this.cropperOptions = { dragMode: 'crop', aspectRatio: 1, autoCrop: true, movable: true, zoomable: true, scalable: true, autoCropArea: 0.8, }; } captureImage() { const options: CameraOptions = { quality: 100, destinationType: this.camera.DestinationType.DATA_URL, encodingType: this.camera.EncodingType.JPEG, mediaType: this.camera.MediaType.PICTURE, sourceType: this.camera.PictureSourceType.CAMERA } this.camera.getPicture(options).then((imageData) => { this.myImage = 'data:image/jpeg;base64,' + imageData; }); } reset() { this.angularCropper.cropper.reset(); } clear() { this.angularCropper.cropper.clear(); } rotate() { this.angularCropper.cropper.rotate(90); } zoom(zoomIn: boolean) { let factor = zoomIn ? 0.1 : -0.1; this.angularCropper.cropper.zoom(factor); } scaleX() { this.scaleValX = this.scaleValX * -1; this.angularCropper.cropper.scaleX(this.scaleValX); } scaleY() { this.scaleValY = this.scaleValY * -1; this.angularCropper.cropper.scaleY(this.scaleValY); } move(x, y) { this.angularCropper.cropper.move(x, y); } save() { let croppedImgB64String: string = this.angularCropper.cropper.getCroppedCanvas().toDataURL('image/jpeg', (100 / 100)); this.croppedImage = croppedImgB64String; } } |
Those are most of the functions, but you can also find all the functions inside the documentation for CropperJS.
Now we just need the appropriate view, and therefore we will use some buttons in the navigation bar for the general operation plus two rows below our cropping area for the additional operations.
The most interesting piece is the angular-cropper
but here we don’t need much more than the predefined options from our class and also the actual image (that we are setting after the camera returns).
Below everything we can also display the result once we exported the crop image as a base64 string, so go ahead and apply these changes to your pages/home/home.html:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
<ion-header> <ion-toolbar> <ion-buttons start> <button ion-button color="danger" (click)="reset()"> Reset </button> </ion-buttons> <ion-title>Ionic CropperJS</ion-title> <ion-buttons end> <button ion-button icon-only color="danger" (click)="clear()"> <ion-icon name="close"></ion-icon> </button> <button ion-button icon-only color="secondary" (click)="save()"> <ion-icon name="checkmark"></ion-icon> </button> </ion-buttons> </ion-toolbar> </ion-header> <ion-content> <button ion-button full (click)="captureImage()" *ngIf="!myImage">Capture Image</button> <angular-cropper #angularCropper [cropperOptions]="cropperOptions" [imageUrl]="myImage" *ngIf="myImage"></angular-cropper> <ion-row *ngIf="myImage"> <ion-col col-4> <button ion-button outline icon-left color="primary" (click)="zoom(true)"> <ion-icon name="add"></ion-icon> Zoom </button> </ion-col> <ion-col col-4> <button ion-button outline icon-left color="primary" (click)="zoom(false)"> <ion-icon name="remove"></ion-icon> Zoom </button> </ion-col> <ion-col col-4> <button ion-button outline icon-left (click)="rotate()"> <ion-icon name="refresh"></ion-icon> 90 deg </button> </ion-col> <ion-col col-2> <button ion-button clear (click)="scaleX()"> Flip X </button> </ion-col> <ion-col col-2> <button ion-button clear (click)="scaleY()"> Flip Y </button> </ion-col> <ion-col col-2> <button ion-button clear icon-only (click)="move(0, -10)"> <ion-icon name="arrow-round-up"></ion-icon> </button> </ion-col> <ion-col col-2> <button ion-button clear icon-only (click)="move(0, 10)"> <ion-icon name="arrow-round-down"></ion-icon> </button> </ion-col> <ion-col col-2> <button ion-button clear icon-only (click)="move(-10, 0)"> <ion-icon name="arrow-round-back"></ion-icon> </button> </ion-col> <ion-col col-2> <button ion-button clear icon-only (click)="move(10, 0)"> <ion-icon name="arrow-round-forward"></ion-icon> </button> </ion-col> </ion-row> <ion-card *ngIf="croppedImage"> <ion-card-header>My Result</ion-card-header> <ion-card-content> <img [src]="croppedImage"> </ion-card-content> </ion-card> </ion-content> |
Now your users are ready to edit their images perfectly with all kinds of operations!
It’s actually super easy to add this functionality to your app, but allowing the editing can make a huge difference in how your users can capture and add the images just like they want it.
You can also find a video version of this Quick Win below.
Hey Simon, Great article.
I followed instruction and created Cropper tool but it is not what I expected, it doesn’t work smoothly on my android device. And it doesn’t allow me to resize the cropper,
Did u find a solution ? I have this same issue, works well on iOS devices but not on android
I get cropper.scaleValY is not a function
and
cropper.scaleValX is not a function
I get this.angularCropper.cropper.getCroppedCanvas(…).toDataUrl is not a function
it is toDataURL not toDataUrl
Blank project is working . existing project is not working
https://uploads.disquscdn.com/images/8e759b59069a68af35f599479d4eaaa72f34ac99ed5e5288d3aaf8b6ad5a3e94.png
i am also getting error like that so, what’s the solution here….
please tell mee
When I try to run ionic serve i get the message error: “Error: Can not find module “cropperjs””.
I tried to add cropperjs tomy project, and then I got the error “Object(…) is not a function”.
This error => Object(…) is not a function
Is caused when you try to use the newest angular-cropperjs functions with angular 2 or 4. In this scenario, you should update to Angular 6 or use angular-cropperjs@0.1.5
This is an outdated tutorial. Does not work. Please update it.
Could you say why you thin it is outdated and what’s not working for you at least?
Getting a template parse error: Can’t bind to ‘cropperOptions’ since it isn’t a known property of ‘angular-cropper’.. Any ideas how to fix that?
Disregard this. I got it figured. Also I should add though if anyone else has the problem of trying to drag on the phone and it stopping after a few pixels to get the whole glitchy moving around thing…You can just add a (touchstart)=”cropperTouchStart($event)” to your element and then in that function make sure you event.stopPropagation() and event.preventDefault(). Works like a charm!
Almost 2 hours trying to find a solution to that issue. Thank you so much.
how did you fix it the ‘cropperOptions’ issues? please share
How did you fix that? please, I don’t find the solution.
I have a template parse error: Can’t bind to ‘cropperOptions’ since it isn’t a known property of ‘angular-cropper’.. Any ideas how to fix that?
Sorry, I found the fix.
what is the fix? please share
I am having this issue after following all the steps. anyone can help me?
Uncaught (in promise): Error: Template parse errors:
Can’t bind to ‘cropperOptions’ since it isn’t a known property of ‘angular-cropper’.
1. If ‘angular-cropper’ is an Angular component and it has ‘cropperOptions’ input, then verify that it is part of this module.
2. If ‘angular-cropper’ is a Web Component then add ‘CUSTOM_ELEMENTS_SCHEMA’ to the ‘@NgModule.schemas’ of this component to suppress this message.
3. To allow any property add ‘NO_ERRORS_SCHEMA’ to the ‘@NgModule.schemas’ of this component. (“ons” [imageUrl]=”myImage” *ngIf=”myImage”>
][cropperOptions]=”cropperOptions” [imageUrl]=”https://i2-prod.mirror.co.uk/incoming/article9863039.ec”): ng:///CropPageModule/CropPage.html@23:37
Can’t bind to ‘imageUrl’ since it isn’t a known property of ‘angular-cropper’.
1. If ‘angular-cropper’ is an Angular component and it has ‘imageUrl’ input, then verify that it is part of this module.
2. If ‘angular-cropper’ is a Web Component then add ‘CUSTOM_ELEMENTS_SCHEMA’ to the ‘@NgModule.schemas’ of this component to suppress this message.
3. To allow any property add ‘NO_ERRORS_SCHEMA’ to the ‘@NgModule.schemas’ of this component. (“yImage”>
][imageUrl]=”https://i2-prod.mirror.co.uk/incoming/article9863039.ece/ALTERNATES/s615/Polish-girl-foun”): ng:///CropPageModule/CropPage.html@23:71
‘angular-cropper’ is not a known element:
1. If ‘angular-cropper’ is an Angular component, then verify that it is part of this module.
2. If ‘angular-cropper’ is a Web Component then add ‘CUSTOM_ELEMENTS_SCHEMA’ to the ‘@NgModule.schemas’ of this component to suppress this message. (“per [cropperOptions]=”cropperOptions” [imageUrl]=”myImage” *ngIf=”myImage”>
[ERROR ->]
][cropperOptions]=”cropperOptions” [imageUrl]=”https://i2-prod.mirror.co.uk/incoming/article9863039.ec”): ng:///CropPageModule/CropPage.html@23:37
Can’t bind to ‘imageUrl’ since it isn’t a known property of ‘angular-cropper’.
1. If ‘angular-cropper’ is an Angular component and it has ‘imageUrl’ input, then verify that it is part of this module.
2. If ‘angular-cropper’ is a Web Component then add ‘CUSTOM_ELEMENTS_SCHEMA’ to the ‘@NgModule.schemas’ of this component to suppress this message.
3. To allow any property add ‘NO_ERRORS_SCHEMA’ to the ‘@NgModule.schemas’ of this component. (“yImage”>
][imageUrl]=”https://i2-prod.mirror.co.uk/incoming/article9863039.ece/ALTERNATES/s615/Polish-girl-foun”): ng:///CropPageModule/CropPage.html@23:71
‘angular-cropper’ is not a known element:
1. If ‘angular-cropper’ is an Angular component, then verify that it is part of this module.
2. If ‘angular-cropper’ is a Web Component then add ‘CUSTOM_ELEMENTS_SCHEMA’ to the ‘@NgModule.schemas’ of this component to suppress this message. (“per [cropperOptions]=”cropperOptions” [imageUrl]=”myImage” *ngIf=”myImage”>
[ERROR ->]<angular-cropper #angularCropper [cropperOptions]="cropperOptions" [imageUrl]="https://i2-prod.mirror"): ng:///CropPageModule/CropPage.html@23:4
at syntaxError (http://localhost:8100/build/vendor.js:96944:34)
at TemplateParser.parse (http://localhost:8100/build/vendor.js:120807:19)
at JitCompiler._parseTemplate (http://localhost:8100/build/vendor.js:130235:37)
at JitCompiler._compileTemplate (http://localhost:8100/build/vendor.js:130210:23)
at http://localhost:8100/build/vendor.js:130112:62
at Set.forEach ()
at JitCompiler._compileComponents (http://localhost:8100/build/vendor.js:130112:19)
at http://localhost:8100/build/vendor.js:129982:19
at Object.then (http://localhost:8100/build/vendor.js:96933:77)
at JitCompiler._compileModuleAndComponents (http://localhost:8100/build/vendor.js:129981:26)
You might need to import it to a different module as it was not found!
Have you found the fix yet?? i have the same error please share if you have solution .
I hope I can get response asap https://ionicacademy.com/ionic-image-crop-cropperjs/#comment-4244588648
It doesn’t work well on my Android device (I’m gonna test this on an iPhone just to see if it’s better). It’s not working smoothy, see this video -> https://gph.is/2BzIH1M
Yes so it’s smooth on an iOS device but not on Android devices.. How to get it smooth on both platforms ?
After a couple of investigations, I discovered that it is a **touchstart** event conflict between ionic gesture/touchstart and cropper. **cropperjs** does not preventDefault and Propagation by default. So therefore add a touchstart event listener to your cropper container div and add **e.preventDefault()** and **e.stopPropagation**. In the below example, i’m using cropper as a component.
cropperTouchStart(event){
event.stopPropagation();
event.preventDefault(); //Most important
}
This solution also applied to many other JS libraries such as: **Fabric.js**.
Hope this helps.
More information in https://github.com/fengyuanchen/cropperjs/issues/143
Why cant you reply?!