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.