Mashape logo hover effect (intro to canvas ImageData)

In the latest incarnation of Mashape, we’ve added a few goodies for people using modern browsers. One of those is the hover effect on the Mashape logo. We’ve found that the logo hover event is the most fired event and scales with other activity on our site. In this blog post, I’ll be going over some basics of pixel manipulation in the HTML5 canvas.

HTML5 provides a lot of access to browser data that was previously inaccessible. Let’s initialize the canvas:

var canvas = document.getElementById("logoCanvas"),
    ctx = canvas.getContext("2d");

The variable “canvas” gets the canvas element from the DOM, and “ctx” gets the rendering context so that we can use 2d canvas functions. We’ll also need to define functions for getting and setting individual pixels:

function getPixel(imageData, x, y) {
     var index = (x + y * width) * 4,
         r = imageData.data[index+0],
         g = imageData.data[index+1],
         b = imageData.data[index+2],
         a = imageData.data[index+3];
     return [r,g,b,a];
}
function setPixel(imageData, x, y, r, g, b, a) {
     var index = (x + y * width) * 4;
     imageData.data[index+0] = r;
     imageData.data[index+1] = g;
     imageData.data[index+2] = b;
     imageData.data[index+3] = a;
}

We assume imageData is an ImageData object, and width is already defined as the width of the canvas. The format of the actual data is an one-dimensional array that stores the red, green, blue, and alpha values of each pixel in sequence from left to right, row by row. That means each pixel takes up 4 values in the array, and in order to translate from Cartesian (x, y) coordinates to this array, we need to get the index by multiplying y by the width and adding x, then multiplying it all by 4.

Now that we have these 2 basic pixel manipulation functions, the fun part begins. Let’s define a function to shift a column of pixels by some number, and then another function that shifts multiple columns like a sine wave:

function shiftColumn(imageData, x, offset) {
     var i, o;
     if(offset < 0) { // going up
         for(i=0; i0; i--) {
             o = getPixel(imageData,x,i);
             setPixel(imageData, x, i+offset, o[0], o[1], o[2], o[3]);
         }
     }
}
function sineTransform(imageData, wavelength, amplitude, phase) {
     var offset;
     for(var i=0; i 0 && x < width) {
         offset = -Math.round(waveHeight*(-Math.cos(i*Math.PI*2/waveWidth)+1)/2);
         x = i+phase;
         if(x > 0 && x < paddedWidth) {
             shiftColumn(imageData, x, offset);
         }
     }
}

The function shiftColumn gets and sets pixels in a column. Depending on the direction, it iterates up or down to be careful not to get already moved pixels. sineTransform applies shiftColumn to multiple columns. That’s it! If you’d like to see a working example of the effect in action, go to mashape.com and hover over the logo. Enjoy!

Mashape logo hover effect

- Mashaper, Dali