Chapter 4. Sprites

Now let's look at doing some simple animations. We'll start with the most basic way of doing this in HTML: sprites. A sprite is a prerendered image that we move around on the screen. It is a limited technique, but you can do surprisingly much with it.

4.1. Polka dots

We'll start with a simple example: Randomly placing red circles onto a yellow browser window. [Demo]

The underlying HTML is quite simple.

<!DOCTYPE html>
<html lang="en">
 <head>
  <meta charset="utf-8">
  <title>Polka dots</title>
  <link rel="stylesheet" href="polka.css" type="text/css">
  <meta name="viewport" content="user-scalable=no, initial-scale=1.0, maximum-scale=1.0, width=device-width">
 </head>

 <body>
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
  <script src="polka.js"></script>
 </body>
</html>

You see that the <body> section of this HTML file is empty except for loading the JavaScript code. There is one addition to the <head> section, though. This new viewport line only has an effect for browsers on mobile devices. Most browsers on mobile devices default to showing a page that is larger than the device, and they use a “viewport” that the user pans around to see the overall page. This added line of HTML indicates that for this page, we want the page to be exactly the same size as the device's screen.

The CSS file polka.css is slightly more complex.

bodyhtml { background-coloryellowwidth100%height100%;
    margin0overflowhidden }
img { positionabsolute }

The first line says to make the body have a yellow background color, to fill the available space horizontally and vertically, to have no margin, and to disable scroll bars. The margin is a bit of whitespace that most browsers add by default around the page, since we normally don't want our text abutting the edges of the window; but in this case, we want the full screen available for our program to manipulate. And the scroll bars are disabled so that, if it happens that our JavaScript happens to add anything that goes off the edge of the window, scroll bars won't appear.

The second rule is actually more subtle: Our JavaScript will be adding <img> elements into the document to act as the sprites, and we want to be able to configure their location on the screen. For this to be feasible, we need to tell CSS how to interpret the location. In this case, we have chosen absolute as its value, which means that the coordinates are counted from the upper left corner of the page (as opposed to being relative to some other location on the page).

One other file that we need is our sprite, a PNG file containing a red polka dot:

And finally, we have our JavaScript file polka.js.

$(document).ready(function () {
    function updateDots() {
        var dotbodyxy;

        if (Math.random() < 0.05) {
            dot = $('<img src="dot.png"></img>');
            dot.width(24);
            body = $('body');
            x = Math.floor((body.width() - 24) * Math.random());
            y = Math.floor((body.height() - 24) * Math.random());
            dot.offset({leftxtopy});
            body.append(dot);
        }
    }

    setInterval(updateDots50);
});

Once the page is loaded, we define the function updateDots and then call the setInterval function. The setInterval function, built into JavaScript, configures the browser to call a function repeatedly at set intervals. In this case, the browser is told to call our updateDots function every 50 milliseconds, a frequency that allows for decent animation.

Each time the updateDots function is called, it generates a random floating-point number between 0 and 1 using the Math.random function. When this is below 0.05 — so 5% of the time updateDots is called — we end up adding a dot into the window, giving an effect of dots appearing at random intervals.

When we are ready to add a dot, we go through the following steps:

  1. We first create a new <img> element using the $ function. Previously, we saw that when you give a string to $, jQuery selects out all the elements that match that selector. That's usually what happens in giving a string to $, but when you give it a string containing some HTML code, it instead creates a new HTML element.

  2. We configure the size of the polka dot by using the width method that jQuery gives it. The polka dot is scaled proportionally, so we don't need to deal with configuring its height.

  3. Then we compute random coordinates where the polka dot should go. We retrieve the <body> element, which fills the browser window, and we use its width and height methods to get the range over which x and y can be chosen.

    Notice that we've used the width method in two different ways. Two lines above, we used width with dot to change the width of the element; but now we use width with body to retrieve the current width. This is typical of jQuery methods: Given no parameters, they retrieve the current value; but given a parameter, they change the value to match the given parameter.

  4. Now we use the offset method provided by jQuery to configure the dot's location on the screen. Our parameter in this case is a dictionary associating the value of x with the string left and the value of y with the string top. The keys left and top are just strings as far as JavaScript is concerned, but the offset method will be looking specifically for the values associated with these words to know where to place the sprite.

  5. Finally, we add the <img> element into the body using the body element's append method. Without this, the image never actually gets into the HTML page, so the window would just stay yellow forever.

4.2. Bubbles

Let's do basically the same thing but with moving dots — which we'll depict as bubbles since they'll be floating upward. [Demo]

Our HTML file is identical to our polka-dot example.

<!DOCTYPE html>
<html lang="en">
 <head>
  <meta charset="utf-8">
  <title>Bubbles</title>
  <link rel="stylesheet" href="bubble.css" type="text/css">
  <meta name="viewport" content="user-scalable=no, initial-scale=1.0, maximum-scale=1.0, width=device-width">
 </head>

 <body>
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
  <script src="bubble.js"></script>
 </body>
</html>

Our CSS file doesn't have much to add, either. One difference is that the background color is written #38C. Since we want a pure sky blue, and there isn't a name for this built into CSS, we instead use a hexadecimal code identifying the color. In this case, we're asking for a color that has 3/15 of the available red, 8/15 of the available green, and 12/15 of the available blue.

htmlbody { background-color#38Cwidth100%height100%;
    margin0overflowhidden }
img { positionabsolute }

Our sprite changes to look more like a bubble:

The major changes are in bubble.js.

$(document).ready(function () {
    function updateBubbles() {
        var bubblebodyx;

        if (Math.random() < 0.05) {
            bubble = $('<img src="bubble.png"></img>');
            bubble.width(24);
            body = $('body');
            x = Math.floor((body.width() - 24) * Math.random());
            bubble.offset({leftxtopbody.height()});
            body.append(bubble);
        }

        $('img').each(function () {
            var pos;
            bubble = $(this);
            pos = bubble.offset();
            pos.top -= 2;
            if (pos.top + bubble.height() < 0) {
                bubble.remove();
            } else {
                bubble.offset(pos);
            }
        });
    }

    setInterval(updateBubbles50);
});

The overall structure here is the same as before: Once the document is loaded, we define an updateBubbles method that is scheduled using setInterval to execute every 50 milliseconds. The beginning of the updateBubbles method is very similar to updateDots: The only difference is that bubbles are always added at the bottom of the window.

But then we have something new: We retrieve all the <img> elements within the page by passing ‘img’ as our selector to the $ function, and then we use the each method. The each method takes a function as a parameter, and it calls this function on each of the elements found in the set with the name this referencing the particular element of the set.

Inside the function passed into each, we perform the following steps:

  1. First we wrap this into a jQuery object by using $(this). This is because jQuery makes this be the raw JavaScript-defined object for representing a node of the tree of HTML elements. Since it's defined by the built-in JavaScript, not by jQuery, it doesn't have all the handy methods that jQuery provides for sets of elements (like width and offset). Since we want to use these methods, we “wrap” this raw object into one of jQuery's wrapped sets by passing the raw JavaScript object into the $ function. The resulting wrapped set has all of the methods we expect from jQuery. (Until now, we've always dealt with wrapped sets when we've worked with HTML elements.)

  2. We then invoke offset on this wrapped set. Since no parameters are given, the offset method returns a dictionary where the string left is mapped to its x-coordinate and the string top is mapped to its y-coordinate.

  3. We deduct 2 from the y-coordinate. This just changes the contents of the dictionary without actually moving the sprite. To actually change the sprite's position, we'll need to call offset with a parameter.

  4. We check whether the sprite has floated off the top of the window by checking whether the y-coordinate of its bottom has decreased below 0. If so, we use the remove method to simply extract the image out of the window.

    This page would still work reasonably well if we just always allowed things to float up continually. But we would find that if we left the page open for a long time, it would slowly eat up more and more CPU time as it has more and more bubbles to update. It is better that we clean up after ourselves.

  5. We reach the else clause when the sprite is still on the window, and in this case we simply update the sprite's position.

4.3. Exercise: Popping bubbles

Now let's make this into a simple but oddly addictive game: Make it so that a bubble pops when you click it. You can do this by using the click method on each newly created <img> element, just as we did with buttons in the previous chapter.

Once you have that, why not have it keep score? I recommend defining two global variables in JavaScript, one tracking how many bubbles were popped and another tracking how many escape off the top of the window. Into your HTML you'd add a paragraph element, and each time either of the variables change your JavaScript would update this paragraph's text using the text method as in the previous chapter.