When building a web application there is a good chance you’re going to need to work with images eventually, even if it is something as simple as allowing a user to upload a profile image. In theory this is a simple task, but in reality, your website theme is probably anticipating images of a certain resolution or aspect ratio. If the user tries to upload an image that doesn’t meet your requirements, it might break your theme.
We’re going to see how to include image manipulation capabilities in your Angular application using the popular cropperjs JavaScript package.
To get an idea of what we’re going to accomplish, take a look at the following animated image:
In the above animation you’ll notice a source image which has a crop box. Altering the crop box will affect the image preview to the right of the source image. This image preview is an entirely new image that represents our manipulations and it can be downloaded as such.
Before getting too involved with this tutorial, the assumption is that you’ve got the Angular CLI installed and configured. For context, I’m using Angular 8.0.2 in this example. If you’re using an older or newer version, things may vary slightly.
From the CLI, execute the following:
ng new image-cropper-example
The above command will start the project creation process. When prompted, choose the defaults as we won’t be doing anything particularly fancy when it comes to Angular.
After the project has been created, navigate into the project and execute the following:
npm install cropperjs --save
The above command will install our cropperjs JavaScript dependency. As a fun fact, jQuery is not a requirement for this example.
Installing the cropperjs
package will only install the JavaScript side of things. We’ll still need the CSS for visualizing our image manipulation box within the source image.
Open the project’s src/index.html file and include the following:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Image Cropping Project</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.1/cropper.min.css">
</head>
<body>
<app-root></app-root>
</body>
</html>
The only change made was in the cropper.min.css file that is now included. You can use it as part of the CDN or download it to be included directly within your project.
Before we get into the core code, let’s create a component to hold our image manipulation code:
ng g component ImageCropper
The above command will create appropriate TypeScript, HTML, and CSS files for our new component. You’ll see how each of these are used in the next step.
We’re going to do most of our development in the new component that we had just created, but before we do that, you might want to find an image to use. For this example, the image should be placed in the project’s src/assets directory.
Open the project’s src/app/image-cropper/image-cropper.component.css file and include the following CSS:
.img-container {
width: 640px;
height: 480px;
float: left;
}
.img-preview {
width: 200px;
height: 200px;
float: left;
margin-left: 10px;
}
The above CSS is not critical to the success of our project, but it makes it a little more attractive to look at. Essentially we are defining the source canvas size and the destination image size.
Now open the project’s src/app/image-cropper/image-cropper.component.html file where we can add the markup for our component:
<div class="img-container">
<img #image [src]="imageSource" crossorigin>
</div>
<img [src]="imageDestination" class="img-preview">
We’re getting a little ahead of ourselves here, but we have two <img>
components, one for our source image and one for our destination image. Each component has a src
variable that we’ll define later in our TypeScript. Notice that the source image has an #image
attribute on it. This is a reference variable that we’ll use within the TypeScript, giving us access to the DOM element. Remember, we can’t just use query selectors in Angular like we can vanilla JavaScript.
With the component HTML out of the way, open the project’s src/app/image-cropper/image-cropper.component.ts file where we’ll do a bulk of the work:
import { Component, OnInit, ViewChild, Input, ElementRef } from '@angular/core';
import Cropper from "cropperjs";
@Component({
selector: 'image-cropper',
templateUrl: './image-croppper.component.html',
styleUrls: ['./image-croppper.component.css']
})
export class ImageCroppperComponent implements OnInit {
@ViewChild("image", { static: false })
public imageElement: ElementRef;
@Input("src")
public imageSource: string;
public imageDestination: string;
private cropper: Cropper;
public constructor() {
this.imageDestination = "";
}
public ngAfterViewInit() {
this.cropper = new Cropper(this.imageElement.nativeElement, {
zoomable: false,
scalable: false,
aspectRatio: 1,
crop: () => {
const canvas = this.cropper.getCroppedCanvas();
this.imageDestination = canvas.toDataURL("image/png");
}
});
}
public ngOnInit() { }
}
The above code is complete, but we’re going to break it down to explain what is happening. It isn’t much, but it is still good to know.
At the top we are importing the cropperjs
package that we had previously downloaded and installed.
Remember that #image
reference from the HTML file? We’re accessing it through the @ViewChild
and mapping it to a variable to be used within our TypeScript code. The @Input
is referring to a possible attribute called src
which we’ll see later.
Because we’re working with elements in the view, we need to wait until the view has initialized before we make any attempts. To do this we can make use of the ngAfterViewInit
method. Inside the ngAfterViewInit
method we initialize our Cropper
using the entire imageElement
that we obtained from the HTML. During the initialization process we can define a few options. There are quite a few to choose from, but for us, we’re going to disable zooming and scaling of our image. In other words we’re only going to allow moving and cropping. We’re also going to define a crop box with a square aspect ratio. None of these are required options. The important option is the crop
method, one of many possible event methods. The crop
method is triggered every time something happens to the crop box. This is important to us because we want to constantly update our preview image.
The preview image is created by getting the cropped canvas and exporting it to an image.
At this point in time our component is done, but not yet being used. To use it, open the project’s src/app/app.component.html file and include the following:
<image-cropper src="assets/angular.png"></image-cropper>
Notice that we’re using image-cropper
which is the selector
value from the project’s src/app/image-cropper/image-cropper.component.ts file. We’re also using src
which was the @Input
that we defined in that same TypeScript file. The src
should reference an image within our src/assets directory.
You just saw how to add image manipulation functionality to your Angular web applications through the cropperjs package. If you’d like to upload these altered images, you might want to check out my previous tutorial titled, Upload Files to Node.js using Angular.
A video version of this tutorial can be found below.