Earlier I posted a quick ditty on the theme of 'my canvas script has failed to getImageData from a canvas. Oh, heavens to murgatroyd, exit, stage left.' Then there was a ninja attack, and a terrible flood, and then I woke up and realized it was all just a dream. Oh, and that I had made some promises to explain how the whole thing worked at some later date. Not the ninja attack and the flood though - those are both forces of nature for which I take no responsibility.
On Topic
Right, the point is - how can we fade images over one another on a canvas. Now, if you're just getting introduced to the wonder that is the HTML5 canvas, I've linked to some references at the end of this article that I used while learning about it. I'm not sure why, but I have this real fondness for the canvas element - it feels like there's a whole realm of possibilities available in it, just waiting to be explored. Games and video that don't require plug-ins - yes, please! Being able to interleave what's happening in 'the game', the canvas area, with events taking place in the browser window surrounding it... There's possibilities for interaction that earlier technologies can't match. And having done some work in Flash (well, Flex), the simplicity of it is nice too - no new language to learn (even if Actionscript is quite familiar to a JAVA-trained programmer), just good ol' javascript.
Of course, the simplicity of it is also its major failing - doing rather simple things can be complicated to achieve. Fading images in and out, you say? The flash designer just points to the timeline and raises a quizzical eyebrow. The html canvas programmer, however, needs to think a little harder.
So, let's break it down step by step, drawing from the canvas script I use on the front page. I'm going to be breaking this article into three parts. This first part will deal with how to get started - setting up the animation timer, load the images we'll need in such a way as to avoid errors. The next two will cover the actual fade effect, and how we switch from there to displaying article links, respectively.
One foot in front of the other
Step One: Get the canvas and set up our animation loop. As you may or may not know, any animation needs to occur within a loop - logic gets updated, positions are changed, the graphics are drawn, rinse and repeat. Think of those flip-book cartoons - each page makes up a part of the animation. How fast you flip the pages will affect how fast the animation goes - this is what we call a 'framerate'. Now that I've bored the experts and mystified the newbies, let's dig in to some code.
var ANIM_TIMING = 66; // 66 milliseconds/frame ~= 15 fps
// 1000 milliseconds per second / 15 fps == 66.67 repeating, see?
var intID; // ID of the interval timer
function drawLogo(){
// Try and get the canvas element, then see if we can draw to it
var canvas = $("#logoCanvas").get(0);
if (!canvas || !canvas.getContext) {
console.log("Error Loading Canvas. Canvas probably not supported.");
return;
}
// Grab the canvas context
var g = canvas.getContext("2d");
...
// Set up the animation loop
var gfunk = function(){fadeInFront(g,images[0],null,x,y,step, logoSwap);};
intID = setInterval(gfunk,ANIM_TIMING);
So what are we doing here? We're getting the canvas (via jQuery, but you could just as easily use getElementById), checking whether we've actually received an object and whether or not we can get its drawing context (and if not, we log our problem and exit out early), and then getting the context proper. Alot of the tutorials you'll see will use var ctx or the like as the variable of choice, but coming from a JAVA background, var g is like an old friend.
After we've got the context, I skip ahead a bit and show you the set up of our animation loop - just a simple setInterval, calling the function we've set up on our ANIM_TIMING interval. Obviously this wouldn't actually do anything yet, but we'll get there.[1]
Step Two: We want to fade some pre-made images over each other, so we need to get those images, right? Also, we need to be sure we don't start drawing until we have all the images, or we could end up erroring out of our canvas effect. So, we'll define the variables we need to get started with, and load the images in such a way that we can be sure they're all loaded before we begin drawing.
// Load the images we want to draw, and when they're loaded, start the first interval timer
var imageSources = new Array(hostLoc+"image/SaneLogo/Step1-Glasses.png",
hostLoc+"image/SaneLogo/Step2-Eyebrows.png",hostLoc+"image/SaneLogo/Step3-SmallSmile.png",
hostLoc+"image/SaneLogo/Step4-MidSmile.png",hostLoc+"image/SaneLogo/Step5-FullSmile.png",
hostLoc+"image/SaneLogo/Step6-Text.png");
loadImages(imageSources, images, function(){
g.globalAlpha = 0.0;
var gfunk = function(){fadeInFront(g,images[0],null,x,y,step, logoSwap);};
intID = setInterval(gfunk,ANIM_TIMING);
});
You see how this links up with step one at the end - what we're doing here is simply declaring where our images are coming from (FYI:var hostLoc = location.hostname), and then calling the function loadImages, which takes our array of imageSources, outputs the resulting image objects into an array called images, and then calls our defined anonymous callback function, in which we set the globalAlpha to 0 for our graphics context, and set up the timer.
So, we need to create loadImages, so that we can rest easy about all our images being loaded before we try to draw them to the canvas.
function loadImages(sources, images, callback){
var totalImages = sources.length;
for (var i=0; i < totalImages; i++){
images[i] = new Image();
images[i].src = sources[i];
}
images[totalImages-1].onload = callback;
}
Okay, that's it for today. Pretty straightforward, I hope - if you have any questions, as usual, that's what the comments are for. If you want to read ahead, remember you're welcome to take a look at the source.
Notes
1 - Those of you familiar with javascript might be wondering about the new alternative to setInterval for animation, requestAnimationFrame. Alot of the browsers have started building in support for an optimized animation call that may out-perform setInterval nicely - but its not a standardized call yet, with the various browser vendors appending webkit or moz or o to the call of window.requestAnimationFrame. In another canvas test I've done, I use a shim to call whatever function is available, handily navigating the multiple browser versions, and I'll cover it in another article.
Parts
References
Dive Into Html5 Canvas - A nice introduction to the canvas element, with welcome examples and humour.
Mozilla Canvas Tutorial - Useful and to the point - will probably be your major point of reference for learning the in-and-outs of the canvas element.