Let's Build An Image Cropper With Angular
- Publish date
In this short guide we'll build an image cropper component with Angular and Pintura. The component will allow us to crop photos in various aspect ratios resulting in better quality user generated content.
We'll start from scratch with a blank project, if you already have a project you want to add an image cropper component, then you can skip to Installing Pintura Image Editor
Let's get started!
Creating A Project
We'll type the following in our terminal to create a new Angular project.
ng new cropper-tutorial
We'll press return a couple times to choose default answers to the tech stack questions. This will start the installation process.
⠹ Installing packages (npm)...
That'll run for a couple seconds until it shows.
✔ Packages installed successfully.
We can now install Pintura.
Installing Pintura Image Editor
Let's install the Pintura component, this will make it super easy to build our image cropper.
npm i @pqina/angular-pintura @pqina/pintura
When this is done we make a small change to the angular.json
file.
// 1. Navigate to: projects > cropper-tutorial > architect > build > options
styles: [
'src/styles.css',
// 2. Add the Pintura style sheet
'node_modules/@pqina/pintura/pintura.css',
]
We save the file and then we can start the development server by typing:
npm start
It'll show the following message if all is fine.
✔ Compiled successfully.
Let's open the project in our browser at http://localhost:4200/
, it'll show the default Angular project page.
Loading Pintura Image Editor
We'll start by editing the app.module.ts
file so we can add the Pintura module.
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
// 1. We import the AngularPinturaModule
import { AngularPinturaModule } from '@pqina/angular-pintura';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
// 2. We add the AngularPinturaModule to the imports list
AngularPinturaModule,
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
Now we'll open the app.component.ts
file so we can add our default editor options.
import { Component } from '@angular/core';
// 1. Import the editor default configuration
import { getEditorDefaults } from '@pqina/pintura';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
title = 'cropper-tutorial';
// 2. Our default editor configuration
defaultOptions: any = {
// The editor defaults, this includes locale and image loaders
...getEditorDefaults(),
// Our custom options (enable dropping and browsing for images on editor click)
enableDropImage: true,
enableBrowseImage: true,
};
}
Next we'll open app.component.html
and we'll replace its contents with:
<div style="height:800px">
<pintura-editor [options]="defaultOptions"></pintura-editor>
</div>
After saving the page your browser will refresh and show "Waiting for image"
If you click the editor you can select an image and start editing.
Configuring Our Cropper
We've now loaded a full blown image editor, we'll probably not need all this functionality so let's slim it down a bit.
We can alter the defaultOptions
object to customize editor functionality
export class AppComponent {
defaultOptions: any = {
// The editor factory settings
...getEditorDefaults(),
// Our preferences
enableDropImage: true,
enableBrowseImage: true,
// Let's only load the crop util
utils: ['crop'],
// We only want square crops
imageCropAspectRatio: 1,
};
}
Receiving The Resulting Cropped Image
Currently nothing happens when we click the Export button. Let's adjust our code so we can see the resulting image.
First we add an <img>
tag to the app.component.html
file, then we'll add the "process"
event handler.
<div>
<!-- 1. Add this image tag, it'll hold the result -->
<img *ngIf="result" [src]="result" alt="" style="max-width:100%" />
<div style="height:800px">
<!-- 2. Add the `handleProcess` event handler -->
<pintura-editor
[options]="defaultOptions"
(process)="handleProcess($event)"
></pintura-editor>
</div>
</div>
Now we update the app.component.ts
so we can handle the resulting image.
import { Component } from '@angular/core';
import { getEditorDefaults } from '@pqina/pintura';
// 1. Import the DomSanitizer so we can use the image Object URL
import { DomSanitizer } from '@angular/platform-browser';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
// 2. Need to use the DomSanitizer
constructor(private sanitizer: DomSanitizer) {}
// 3. This will hold the resulting image
result?: string = undefined;
// 4. This will handle the process event
handleProcess($event: any) {
// `dest` will hold a file object, but we need a URL
const objectURL = URL.createObjectURL($event.dest);
// We'll sanitize the URL and use it as a string
this.result = this.sanitizer.bypassSecurityTrustResourceUrl(
objectURL
) as string;
}
// Our default options, nothing changed here
defaultOptions: any = {
...getEditorDefaults(),
enableDropImage: true,
enableBrowseImage: true,
utils: ['crop']
};
}
Now when we edit the image the resulting image will be shown above the editor.
Cropping A Square Image
Let's adjust our code so we can crop a square image. We want to tell the editor that when an image loads we only allow a square crop, so let's do that now.
<div>
<img *ngIf="result" [src]="result" alt="" style="max-width:100%" />
<div style="height:800px">
<!-- 1. Add the #myEditor ref -->
<!-- 2. Add the `handleLoad` event handler -->
<pintura-editor
#myEditor
[options]="defaultOptions"
(load)="handleLoad($event)"
(process)="handleProcess($event)"
></pintura-editor>
</div>
</div>
We now have to handle the load event in the app.component.ts
file.
// 1. Import ViewChild so we can reference the editor
import { Component, ViewChild } from '@angular/core';
import { getEditorDefaults } from '@pqina/pintura';
import { DomSanitizer } from '@angular/platform-browser';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
// 2. Add ViewChild decorator so we can reference the editor
@ViewChild('myEditor') myEditor?: any = undefined;
constructor(private sanitizer: DomSanitizer) {}
result?: string = undefined;
// 3. We handle the load event, when an image is loaded the crop aspect ratio is set to 1.
handleLoad($event: any) {
this.myEditor?.editor?.imageCropAspectRatio = 1;
}
handleProcess($event: any) {
const objectURL = URL.createObjectURL($event.dest);
this.result = this.sanitizer.bypassSecurityTrustResourceUrl(
objectURL
) as string;
}
// Our default options, nothing changed here
defaultOptions: any = {
...getEditorDefaults(),
enableDropImage: true,
enableBrowseImage: true,
utils: ['crop']
};
}
That's it! I hope you found this an interesting read, feel free to reach out with any questions.
You can find a Pintura Angular example project on GitHub it includes a link to a live code environment where you can run the project online.