Article

Hacking JavaScript for Fun and Profit: Part I

Page: 1 2 3 Next

Defining Behavior by Class Name

Now we have the JavaScript class written, wouldn’t it be cool if we could tell a HTML element to become a WalkingSprite object just by giving it a specific class name? In JavaScript 1.6, you can easily find all DOM elements with a certain class name using the document.getElementByClassName function. However, most browsers don’t support version 1.6 yet. Luckily, Prototype provides us with the $$ function—pass it a CSS selector and it will return an array of all matching elements.

Take a look at the following code:

var WalkingSprite = Class.create({  
 x: 0,  
 y: 0,  
 element: null,  
 
 initialize: function(element) {  
   this.element = element,  
   this.x = element.offsetLeft,  
   this.y = element.offsetTop  
 },  
 
 walk: function(steps) {  
   this.x += steps;  
 }  
});  
 
var KoopaSprite = Class.create(WalkingSprite, {});  
 
var koopas = new Array();  
var koopaElements = $$('koopa');  
for(el in koopaElements) {  
 koopas.push(new KoopaSpriteSprite(el));  
}

First we create the WalkingSprite class, and then the KoopaSprite class that uses the WalkingSprite class as its parent. Next, we create an array of KoopaSprite objects by selecting all the elements within the document that have the class name “koopa”.

Now, we have an array of KoopaSprite objects, with references to corresponding DOM elements (this becomes important later). What we have done here is the basis of Unobtrusive JavaScript. Now that we have dynamically found the HTML elements that we are interested in, we can bind events (such as onclick and onfocus), restyle them, or make them disappear!

Making Motion Pictures

Since we aren’t writing a text-driven adventure game, we will need a way of animating our characters. This goes beyond moving them around the screen, which will be covered later. It would also be good if we could make the characters look like they are walking, jumping, or ducking. To do this, we will call on an old CSS trick: the background position hack.

The idea is simple: we build a ribbon of images that form the frames of our animation, and then cycle through them by shifting them left and right x number of pixels. Here’s an example background image:

The background image for this demo

As you can see, we have 12 frames in one image, each 48 pixels apart. If we had a div of class mario, the CSS for some of the different frames may look like this:

div.mario {  
 width: 45px;  
 height: 45px;  
 background-image: url(mario.gif);  
 background-repeat: no-repeat;  
 background-position: 0 0;  
}  
 
div.mario.jump-left {  
 background-position: -90px 0;  
}  
 
div.mario.duck-right {  
 background-position: -180px 0;  
}

You may have seen this technique before to create flickerless rollovers. Back in the old days, you would create image rollover effects using a small piece of JavaScript that changed the src value of an image tag when the onmouseover event fired. Yet the first time you did it, the browser still needed to download the image from the server, which often caused flickering. It was possible to preload the images, but it was all a bit clunky. The superior CSS technique allowed the designer to load all of the rollover states in one image, and use the :hover pseudo-class to create a separate CSS rule to shift the background, giving smooth transitions without JavaScript.

In our game engine though, we will be changing the position of the background image using JavaScript. To set the background position in JS, you manipulate the element’s style.backgroundPosition attribute. The following code creates a new class called MarioSprite that adds a render function to the parent WalkingSprite class. This new function is called repeatedly with a time delay, and will animate Mario walking using two frames:

var MarioSprite = Class.create(WalkingSprite, {  
 renderState: 0;  
 
 render: function() {  
   if(this.renderState == 0) {  
     this.element.backgroundPosition = '0px 0px';  
     this.renderState = 1;  
   } else {  
     this.element.backgroundPosition = '-48px 0px';  
     this.renderState = 0;  
   }  
 }  
});

If you liked this article, share the love:
Print-Friendly Version Suggest an Article

Sponsored Links