Image Manipulation with HTML5 Canvas - Part I

For our upcoming website relaunch, I did some interesting experiments with the HTML5 canvas element.

I wanted to load screenshots or other images into a canvas element and apply two different manipulations on those. First of all my goal was to generate a wet floor / reflection effect for any arbitrary image and second of all I dealt with projective transformation to create some kind of perspective view of a 2D image. Whereas it's easy to find lots of good tutorials dealing with reflection effects,  my second idea, the projective transformation, turned out to be much more complicated. In order to keep this blog post short, I m going to split it up in two parts - starting now with the wet floor effect in part 1 and continuing with projective transformation later on.

HTML 5 Logo

Creating a Reflection

In my opinion a good place to start is the Mozilla Developer Network's (MDN) article on "Using Images". It shows how to load images into canvas elements and how to apply simple affine transformations like rotate, scale or position. A very powerful operation provided by the canvas element is slicing. You can cut parts out of an image, transform these slices and paste them back into the original image. This is one of the keys in order to generate a reflection effect.

Step 1

Let's have a look at it step by step. Step 1 is basically loading an image into a canvas element. This can be achieved with the following JavaScript snippet. One, in my opinion, important fact is the use of  the "onload"-Handler for images. If you are not executing the image operations after the onload event, you might experience some strange effects like empty canvas elements, which display images only after you hit the browser's refresh button once before... yep, that happened to me.

var context = document.getElementById("canvas1").getContext("2d");
var img = new Image();
img.src = "images/screenshot.png";
img.onload = function() {
    context.drawImage(img, 0, 0);

First the canvas element is retrieved by it's ID and an image object is created. Afterwards the image is drawn onto the canvas. Therefore within the HTML page a canvas element, with the requested ID (canvas1) is needed.

<canvas id="canvas1" width="150" height="150"></canvas>

The result should look like the example image below. Nothing special so far...

Canvas Screenshot

Step 2

In the next step we create a copy of the image, mirror the copy and paste the mirrored version to the bottom of the original. Therefore we extend the img.onload function by the following lines of code.

/* mirrow image horizontally and move it to the bottom of the first image */
context.scale(1, -1);
context.translate(0, -img.height);

This snippet flips the image around and moves it to the bottom of the original. Afterwards the new image has to be drawn at the correct position, which can be done by the drawImage function.

context.drawImage(img, 0, -img.height, img.width, img.height);

Note that you have to double the height of the canvas element, as our canvas now takes up double space (original + mirror).

<canvas id="canvas1" width="150" height="300"></canvas>

Afterwards the new image should look a bit like the example below.

Canvas Screenshot

Step 3

Because we don't want to have a reflection, that is as high as the original image (although we could do that), we have to cut the mirrored image. In this example we are going to cut it in half by using the already mentioned slicing operation. Slicing can also be achieved with the drawImage function by adding some additional parameters. Again, the MDN article proves to be a good source of information. There the drawImage function is defined as follows.

drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)

The first parameter  is the image to draw, the following four parameters define the section of the original image to draw. The last four parameters define the coordinates and size of the destination image to draw. In order to cut the mirrored image of our example in half , the drawImage function has to be called with the following parameters.

/* define parameter */
var sx = 0;
var sy = img.height * 0.5;
var sWidth = img.width;
var sHeight = img.height * 0.5;
var dx = 0;
var dy = -img.height * 0.5;
var dWidth = img.width;
var dHeight = img.height * 0.5;
/* call drawImage */
context.drawImage(img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);

And this should be the result.

Canvas Screenshot

Step 4

Only one more step to go. Now we are going to add a transparency gradient, going from 25% to 100% black, as an overlay over the mirrored part of the image. First of all we define the mirror's height as a helper var and flip the canvas (back) around.

var mirrorHeight = img.height * 0.5;
/* flip canvas around */
context.scale(1, -1);

Now we can create the gradient. It is a linear gradient, which is as high as the mirrored part of the image. It starts with 25% black and ends with 100% black. In this case black is the background color I used. If your background is for example white, use rgba( 255, 255, 255, 0.25 ) and rgba( 255, 255, 255, 1).

/* create gradient */
var gradient = context.createLinearGradient( 0, 0, 0, mirrorHeight); 
gradient.addColorStop( 0, 'rgba( 0, 0, 0, 0.25 )' ); 
gradient.addColorStop( 1, 'rgba( 0, 0, 0, 1.0 )' );

Assign the created gradient to a rectangle and fill the canvas with this rectangle.

/* fill rectangle with gradient */ 
context.fillStyle = gradient; 
context.rect( 0, 0, img.width, mirrorHeight ); 

In the end the final image should look like this:

Canvas Screenshot

Unfortunately this only works if your background consists of one single color (like black in this example). If you have your canvas drawn on a background image this might not be the right approach.

Sven Hoffmann

Sven Hoffmann

2011-09-02 • 02:30 PM

Web Technology