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.
body, html { background-color: yellow; width: 100%; height: 100%;
margin: 0; overflow: hidden }
img { position: absolute }
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 dot, body, x, y;
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({left: x, top: y});
body.append(dot);
}
}
setInterval(updateDots, 50);
});
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:
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.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.Then we compute random coordinates where the polka dot should go. We retrieve the
<body>
element, which fills the browser window, and we use itswidth
andheight
methods to get the range over whichx
andy
can be chosen.Notice that we've used the
width
method in two different ways. Two lines above, we usedwidth
withdot
to change the width of the element; but now we usewidth
withbody
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.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 ofx
with the string left and the value ofy
with the string top. The keys left and top are just strings as far as JavaScript is concerned, but theoffset
method will be looking specifically for the values associated with these words to know where to place the sprite.Finally, we add the
<img>
element into the body using the body element'sappend
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.
html, body { background-color: #38C; width: 100%; height: 100%;
margin: 0; overflow: hidden }
img { position: absolute }
Our sprite changes to look more like a bubble:
The major changes are in bubble.js.
$(document).ready(function () {
function updateBubbles() {
var bubble, body, x;
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({left: x, top: body.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(updateBubbles, 50);
});
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:
First we wrap
this
into a jQuery object by using$(this)
. This is because jQuery makesthis
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 (likewidth
andoffset
). 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.)We then invoke
offset
on this wrapped set. Since no parameters are given, theoffset
method returns a dictionary where the string left is mapped to its x-coordinate and the string top is mapped to its y-coordinate.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.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.
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.