Interactive Typography Effects with HTML5

With HTML5 gaining popularity, this tutorial outlines what is really just the tip of the iceberg that is interactive design. In this tutorial I will go over the development of dynamic, and generative banners to give your website that little extra wow!

With HTML5 gaining popularity, this tutorial outlines what is really just the tip of the iceberg that is interactive design. I will go over the development of dynamic, and generative banners to give your website that little extra wow!

If you are interested in reading more about the HTML5 spec, this is a great resource: Dive Into HTML5 by Mark Pilgrim

Markup

The HTML markup is very simple, since we will be doing all the work with JavaScript, on the canvas element:

	<canvas id="banner"></canvas>

JavaScript

As expected, it will be JavaScript doing all the heavy lifting here. But first and foremost, we will need to check if our users are using a modern browser that supports the canvas element. If it’s not supported, we want to dish up the next best thing: a static image. We will be using Modernizr to check on this.

Variables

Let’s start defining some variables:

		//The words to be written on the banner
		var keyword 	= "HONEY",
		canvas, context,	
		bgCanvas, bgContext,
		density 	= 13,
		particles 	= [],
		colour 		= '#fff0a4',
		mouse 		= { x:0, y:0 },
		isDrawing 	= false,
		canvasW, canvasH;

A little explanation: The keyword here is going to be the word which you want to display on the banner.

The canvas, is a variable which will hold the HTML canvas element. This will be assigned later. The context is what allows us to drawn and write to the canvas.

The density variable holds how many pixels we want to have (both horizontally and vertically) between each banner particle.

The particles array will store all the particles and their respective settings.

The colour variable is the colour that each particle will have.

Finally, the isDrawing variable let’s us know if we are hovering over the banner, and the canvasW and canvasH are the canvas’ width and height respectively.

Setting up the banner

The first step is to call the reload function which we will also call on window resize:

this.initialize 	= function( canvas_id ) {

		reload( canvas_id );
		
		window.onresize = function(event) {
			reload( canvas_id );
		}

};

In the reload function we initialize the canvas and context and set the respective sizes. Then we call the prepare, setupParticles and draw functions.

var reload			= function( canvas_id ) {
	
	canvas 			= document.getElementById( canvas_id );

	if ( !window.HTMLCanvasElement ) return false;
	
	context 		= canvas.getContext('2d');
	
	canvasW			= window.innerWidth;
	canvasH			= 300;
	
	canvas.width 	= canvasW;
	canvas.height 	= canvasH;
	
	bgCanvas 		= document.createElement('canvas');
	bgContext 		= bgCanvas.getContext('2d');
	
	bgCanvas.width 	= canvasW;
	bgCanvas.height = canvasH;
	
	prepare();
	setupParticles();
	draw();

};

The first thing to note here, is that the function takes a string variable, which should match the element ID in the HTML. In our case, we would pass “canvas-banner”.

As you can see, both, the background canvas and the main canvas are set to the same size. We will get into why we are using two canvas elements in the next section.

The Background Canvas

example2

Ok, now I have some explaining to do: The reason why we have two canvases here, is more or less the core of these banners. The background canvas is where you will be printing the text, or images. Afterwards, we will scan over the image data (pixel by pixel) of the canvas. If we run into a black pixel we will know that we have drawn on this location, and hence, will create a particle in its place.

var prepare 		= function() {
							
	//Chose the font, and size that we want.
	bgContext.font = "300px 'Jockey One'";
	
	//Fill the keyword text onto the canvas.
	bgContext.fillText(keyword, ( canvasW / 2 ) - ( Math.round( bgContext.measureText(keyword).width/2 ) ) , 260 );
	
};

The fillText command above, takes three parameters: The first being the text to write, the second and third being the x and y co-ordinates respectively.

Next, we will create our particles. These will soon be drawn onto the main canvas, but for now, we will just store them in our particles array:

var setupParticles 	= function() {
		
	particles = [];		
	
	//Declare our local variables
	var imageData, image_Data, 
		pixel,
		width	= 0,
		i		= 0,
		slide 	= false;
		
	//Get the image data - from (0,0) to the edges of the canvas
	imageData = bgContext.getImageData( 0, 0, canvasW, canvasH );
	image_Data= imageData.data;
	
	for( var height = 0; height < canvasH; height += density ) {
		
		++i;
		slide 	= ((i % 2) == 0);
		
		width	= 0;
		
		if (slide == true) {
		
			width += 6;
		
		}
		
		//Iterate horizontally over the image data
		for( width; width < canvasW; width += density ) {
		   
			//Get the pixel located at our current iteration
			pixel = image_Data[ ( ( width + ( height * canvasW )) * 4 ) - 1 ];
			  
			//Pixel has been drawn on.
			if( pixel == 255 ) {

				//Add the coodinates and colour to our particle array.
				particles.push({
					colour	: colour,
					x		: width,
					y		: height
				});
				
			}
		}
	}    
};

Yes, that is a large chunk of code, I’ll go over the tricky parts now.

Really, all we are doing is looping over every n-th pixel, where n is the density set at the start.

getImageData takes four values, the x and y of the top left corner, as well as for the bottom right. In our case, we passed it two zeros (the very top left) and the canvas width and height, which are the coordinates of the very bottom right. It returns a very big list, containing the color data of every single pixel on the canvas. Why are we multiplying this by four? Because within this list, each pixel is represented by four values, red, green, blue and an alpha value.

By default, all the canvas imageData contains, is a long list of zeros – by drawing on this canvas (in our case, using fillText), the pixels now contain proper color data.

Finally, we have pushed the x and y coordinates into a simple dictionary, which we will now use to draw our banner.

Drawing the banner

example3
Here, we finally pull everything together. With our list of x and y positions, stored in the particles array, we can draw out the banner.

var draw = function() {
			
	context.clearRect( 0, 0, canvasW, canvasH );
	
	var dx, dy, sqrDist,
		scale = 1;
	
	for ( var i = 0, len = particles.length; i < len ; ++i ) {
		
		...
		
		context.beginPath();
		
		context.moveTo( x, y - height / 2 );
		context.lineTo( x + width / 2, y - height / 4 );
		context.lineTo( x + width / 2, y + height / 4 );
		context.lineTo( x, y + height / 2 );
		context.lineTo( x - width / 2, y + height / 4 );
		context.lineTo( x - width / 2, y - height / 4 );
		context.lineTo( x, y - height / 2 );  
		
		context.closePath();
		context.fill();
		
	}
	
};

We loop through each of the particles we stored previously to set the color and to draw a shape on that (x,y) point.

With that, we have a very simple static banner… now for the fun part, let’s add some interactivity.

Adding Interactivity

example4
The fun thing about this is that we can be as simple, or complex as we want. The first thing we will want to do though, is track the user’s mouse position while it is over the banner.

		
var mouse = {x:0, y:0, o: false};

The mouse variable will be updated by two functions, one tracking the movement, and one tracking whether the mouse has left the canvas element:

var MouseMove 		= function( e ) {
    	
	mouse.x = e.offsetX || ( e.layerX - canvas.offsetLeft );
	mouse.y = e.offsetY || ( e.layerY - canvas.offsetTop );
	
	if( !isDrawing ) {
		
		isDrawing = true;
		drawTimeout = setTimeout( function() {
			draw();
			isDrawing = false;
		}, 60);
		
	}
	
};
	
var MouseOut 		= function(e) {
			
	isDrawing = false;	
	clearTimeout( drawTimeout );
	draw();
	
};

In order to make these work, we also need to attach JavaScript events to our canvas element. These will go right back up into our reload function.

	canvas.addEventListener('mousemove', MouseMove, false);
	canvas.addEventListener('mouseout', MouseOut, false);

Now that we can track the mouse, we need to change up our draw function a little, based on how we want to interact with the mouse.

...

var p   	= particles[i];
		
dx 			= p.x - mouse.x;
dy 			= p.y - mouse.y;

// distance from mouse to particle
sqrDist 	= Math.sqrt( dx * dx + dy * dy );

( isDrawing ) ? scale = Math.max( Math.min( 3 - ( sqrDist / 10 ), 10 ), 1 ) : scale = 1;

var width  	= density / scale - 4,
	height 	= density,
	x 		= p.x,
	y 		= p.y;

context.fillStyle = p.colour;

...

Above, we calculate the distance between the current particle and the mouse. Then we update the variable scale to fall between 1 (meaning no scaling) and the distance between the mouse and the particle (but not bigger than 10).

All that’s left now, is to tell our canvas to draw at regular intervals, rather than just once at the start. At risk of getting too complicated I’m just going to keep it simple and use setTimeouts when the user hovers over the banner – but if you are interested in looking more into canvas animations, the function you will be looking for is requestAnimationFrame

Let’s get some color


We have a neat working banner, why not add a little color. Fortunately, we have already done most of the work here, we only need to change 2 lines.

	
//A variable to store each of the colors we want to use
var colors = ['#fff0a4', '#ffe98f', '#ffcf52', '#fbad1d', '#c48d1c'];

	...

//When creating the particles, we assign a color from this list.
color: colors[Math.floor(Math.random() * colors.length)],

And there we have it! A unique, generative and colorful banner, made just for you. Take a look through the demos to see a few other examples, of just what you can do.

Tagged with:

Tim Holman

Tim Holman, an Australia based programmer, interested in new technology, the world and the future. Check out his web experiments in his interactive portfolio.

Stay up to date with the latest web design and development news and relevant updates from Codrops.

Feedback 36

Comments are closed.
  1. Wow… that’s some clever coding. It’s really nice to see what’s possible with HTML5 and it looks to me more and more that the possibilities are endless.

  2. I still do not know if I should still make the decision to learn flash. I know flash is still going to be around for sometime but things like this makes you confuse.

    Good job Tim

  3. Hi
    I can’t change the height , when i do the word is cropped , i end up with huge padding top 🙁

  4. This is toooo awesome! You have an awesome creativity. I have been wanting to try out HTML5 Canvas for quite some time now and I guess I was waiting for something like this 😛
    Thanks!

  5. Really nice text effects and tutorial.
    Does anyone know how I could intergrate this into a WordPress site?