Thursday, March 28, 2024

Crop Images Online using the Cropper.js Library

Crop Images Online using the Cropper.js Library

The introduction of the HTML5 canvas element opened a lot of doors in the realm of client-side image processing. The secret is the 2D context API; it offers up a number of functions that give you the ability to draw pretty much anything you can dream up. It’s also quite handy for manipulating your existing images. For instance, you can convert color images to black & white as well as apply effects like Negative, Blur, and Rotate . One of the more in-demand image operations is cropping. Although certainly possible, writing the code to do it is slightly above the pay grade of the average coder, who is not an expert in image manipulation – myself included! Thankfully, a number of excellent client-side image cropping libraries have emerged to simplify our lives. In today’s article, I’ll describe the process of selecting a local image and cropping it using one of the most popular cropping libraries: cropper.js.

One Library, Two Flavors

In researching this article, I quickly discovered that there are two versions of cropper.js: one for native JS and the other for use with jQuery. It doesn’t help that both libraries are named cropper.js. The distinguishing feature is the directory, which is named “cropperjs” for the native JS version, and simply “cropper” for the jQuery edition. For this tutorial, I’ll be using the latter (I can’t help it, I love jQuery).

The HTML

Our page will be very simple, with a canvas, a few buttons, and a result DIV for displaying our cropped images.

<p>
  <input type="file" id="fileInput" accept="image/*" />
  <input type="button" id="btnCrop" value="Crop" />
  <input type="button" id="btnRestore" value="Restore" />
</p>
<div>
  <canvas id="canvas">
    Your browser does not support the HTML5 canvas element.
  </canvas>
</div>           

<div id="result"></div>

The CSS

The important thing to note here is that the authors of cropper.js recommend limiting the image width in order to avoid overflowing the container:

img {
  max-width: 100%; /* This rule is very important, please do not ignore this! */
}

#canvas {
  height: 600px;
  width: 600px;
  background-color: #ffffff;
  cursor: default;
  border: 1px solid black;
}

The JavaScript

Uploading the File

The File Input control’s onchange event is fired whenever the referenced file(s) changes, i.e, right after the File Select dialog closes. In our handler we can validate the file type using the File’s type property. It returns the MIME type, such as “text/plain”, “application/pdf”, or “image/gif”. In fact, all image types start with the “image/” identifier, so we can test for it using a Regex:

var canvas  = $("#canvas"),
    context = canvas.get(0).getContext("2d"),
    $result = $('#result');

$('#fileInput').on( 'change', function(){
    if (this.files && this.files[0]) {
      if ( this.files[0].type.match(/^image//) ) {
        //process the image...
      }
      else {
        alert("Invalid file type! Please select an image file.");
      }
    }
    else {
      alert('No file(s) selected.');
    }
});

Drawing the Image on the Canvas

Once we’ve ascertained that the selected file is indeed an image, we must use the FileReader API to fetch its contents using the readAsDataURL() method. It performs an asynchronous read of the file contents and fires the onload event once the read operation is complete. At that time, the result property contains a data URL string that encodes the file’s data.

Within our FileReader instance’s onload handler, we can assign the result property a new Image object. That allows us to display the image in the canvas by invoking the canvas’s drawImage() function within the image’s onload:

var reader = new FileReader();
reader.onload = function(evt) {
   var img = new Image();
   img.onload = function() {
     context.canvas.height = img.height;
     context.canvas.width  = img.width;
     context.drawImage(img, 0, 0);
    
     //instantiate the cropper here...
   };
   img.src = evt.target.result;
};
reader.readAsDataURL(this.files[0]);

Instantiating the Cropper

Using the jQuery syntax, we invoke the cropper constructor as a member method of the canvas jQuery element. The cropper can also be instantiated as a member of an image element, but a canvas is better suited for our purposes. The constructor accepts many options within the options object argument, but we’ll just set the aspect ratio of the cropper tool:

var cropper = canvas.cropper({
  aspectRatio: 16 / 9
});

Cropping the Image

At last we get to the main goal of this tutorial, which is of course to create a new image from a cropped portion of the original image. All it takes is a couple of lines of code. First, we get the base 64 encoded image data by extracting the cropped canvas section. Cropper is a little unusual in that you invoke its methods by passing the method name to the universal cropper constructor. Hence, calling canvas.cropper('getCroppedCanvas') returns the cropped canvas. Form there, we can call the canvas element’s toDataURL() function to get its base 64 encoded data string. That data can then be assigned directly to a new image’s src property:

$('#btnCrop').click(function() {
  // Get a string base 64 data url
  var croppedImageDataURL = canvas.cropper('getCroppedCanvas').toDataURL("image/png");
  $result.append( $('<img>').attr('src', croppedImageDataURL) );
});

For the demo and full source code, visit codepen.io.

Conclusion

In a future article, we’ll learn how to do a few other things, such as how to make the cropping container responsive so that it does not retain the same aspect ratio, as well as how to zoom in and out on our image.

Rob Gravelle
Rob Gravelle
Rob Gravelle resides in Ottawa, Canada, and has been an IT guru for over 20 years. In that time, Rob has built systems for intelligence-related organizations such as Canada Border Services and various commercial businesses. In his spare time, Rob has become an accomplished music artist with several CDs and digital releases to his credit.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Popular Articles

Featured