
Bitmap, Buffer, Character, Layer, Overlay, Shape, Sprite, Z-Index
Not for the first time, Pam is negotiating away a stack of requirements from the project goals. Having successfully convinced the client "Security Features" was never really worthwhile, she drags a "Magic Eraser" Sprite from the tool palette to the "Security Features" requirement, shakes the mouse a couple of times, and the eraser is animated for a second as the requirement fades away.
Rich visual displays are often the most effective way to present information.
Information frequently changes as the user interacts and new data is received from the server, hence the visual display needs to frequently update.
It's expensive to continuously download new visual content.
Augment the display with "Sprites": small, flexible, icon-like blocks of content. Typically, the Sprites are images, but they may also be div elements containing text or images or a combination of both. The name refers to the Sprites used in traditional graphics programming, particularly gaming. They are the little graphics representing players, enemies, and physical objects, capable of moving around a scene and animating themselves to reflect current activity. The entire scene does not need to be recomputer each time, only the Sprites. DOM elements are not (yet) capable of more advanced features such as arbitrary rotation, but many aspects of Sprites can actually be translated to a web context.
Google Maps is a case in point. Search for "museum new york" and you'll see a set of thumbtack Sprites appear on the map, one for each museum. Then click on a thumbtack, and a new speech balloon Sprite will show with the precise address. Using Sprites, Google spares itself the burden of preparing images for every possible configuration, or generating images on the fly. All it needs to do is ensure there's a basic display of the map in question, and overlay the thumbtacks and speech balloons.
Sprites are often implemented as div elements, or simply images. They usually appear "in front of" the rest of the document and are sometimes partially transparent. Following are the relevant CSS properties (see also “Page Rearrangement”:
zIndex determines how elements are stacked, and Sprites usually have a high value in order to appear in front of other elements.
left, right, top, and bottom are often used to position a Sprite. position can also be set to influence how the browser interprets these settings.
opacity is often used to make the Sprite partially transparent. (IE requires the alpha filter workaround.)
backgroundImage is used to select a background image when the Sprite is a div. As long as there is no foreground content, it is the background image that is shown. The image need not be rectangular, because a transparent GIF file can be used to show any arbitrary shape.
You can also animate the Sprite by rapidly changing its appearance. Unfortunately, Javascript cannot easily control animated GIFs, so that's not a viable option. However, animation is easy enough with some “Scheduling”. A simple technique is to set up a recurring "changeImage()" action, in which the element's backgroundImage is continuously run through a cycle of different images. Ideally, you should preload the images first to avoid a slow cycle the first time round. Several tricks are available for preloading, most commonly by setting an invisible image's source to point to the new image. An alternative animation technique is outlined in the Code Examples below.
Sprites are usually images, but what file format to use - JPEG, GIF, or PNG? There are several considerations:
Portability
Image quality
File size
Transparency
JPEG may be the most common format on the web, but is ruled out for most Sprites. It doesn't support transparent pixels, so the Sprite must be rectangular, and it is not actually very effective at storing the kinds of images typically used for Sprites: small, cartoonish line drawings. So JPEG is only a consideration in the rare case you have a fairly large, rectangular, photo-like Sprite.
This leaves GIF and PNG as the most common choices. An intellectual property cloud used to hang over GIF, but it's no longer a valid reason to avoid GIF as the patent in expired in 2003. PNG does have some technical advantages over GIF, particularly variable transparency, but unfortunately that's not been correctly implemented in IE6 and earlier. So to support older IE editions, you'll need to manipulate opacity if you want to achieve transparency, regardless of the format. Also, even older versions of IE don't support PNG at all.
In summary, GIF should be the default choice; only use JPEG or PNG if there is a specific reason.
Ongoing animation can be distracting, but a quick animation is an effective way to convey what's happening. Consider how the "one-second" visual effects might be used:
“One-Second Spotlight”: You might Materialise a Sprite when it first appears, or you might Fade In the Sprite when its subject to some action.
“One-Second Mutation”: You might Form a Sprite when it first appears, or you might rapidly Metamorphise it in a cycle, to provide the illusion of continuous change.
DHTML Lemmings by Tino Zijdel ("Crisp") is a game which uses Sprites in their traditional gaming sense. Lemmings are shown performing different actions, and the characters change shape as they move around the game space. The entire thing is orchestrated with DOM and CSS scripting.
As discussed in the Solution above, Google Maps overlays the basic map with thumbtacks to highlight certain locations. As an interesting variant, look at the use of thumbtacks by Housing Maps, the Maps-Craigslist mashup overviewed in “Cross-Domain Proxy”.
Quek is a fun use of Sprites as chat avatars (Figure 1.66, “Quek”). The application is a "chat-and-surf" program, meaning that Quek users can chat about a website they're visiting at the same time. Traditionally, such applications have involved “Richer Plugin”s, but Quek avoids any of that. It works by rendering the entire target website, so you're always actually on a Quek page, but the page looks like the same as the target site. Logging into Quek, each user is assigned a unique Sprite consisting of an image, a text ID underneath the image, and an input box (visible only on the user's own Sprite). Typing in the input box animates the Sprite image, and clicking Enter submits the message for all to see. The user is free to drag the Sprites around the page. So the appearance is several Sprites moving around and talking to each other, with the web page as a backdrop.
DHTML Lemmings involves the most sophisticated use of Sprites I know of. The analysis here is only a rough summary of what's achieved. Note that the code uses some special handling for IE, which the author, Tino, explained to me is due to a bug where IE (at least some versions) bypasses the cache upon any background style change. To keep it simple, the following discussion skims overt IE-specific handling.
In DHTML Lemmings, each lemming is represented by a Lemming object, initialised with a number to identify the lemming, and an initial position corresponding to the place where the lemming first appears on this particular level. Various other state information is also tracked:
function Lemming(i,top,left) {
...
this.top = top;
this.left = left;
...
this.number = i;
...
}
Lemmings themselves are regular Javascript objects and not visible in the browser. To visually depict the lemming, a div element is created and made an attribute of the lemming, simply known as l. Thus, we might say the Lemming model "wraps" a Sprite as a means of separating semantic content from visual content. Each of these Sprites resides in the playground, an area representing the game space where the lemmings roam around. Both the playground and the Sprites are rendered with absolute positioning. Once the Sprite element is created, event handlers are also added to support the gameplay:
function Lemming(i,top,left) {
...
var l = document.createElement('div');
l.number = i;
l.className = 'lemming';
l.style.top = top+'px';
l.style.left = left+'px';
...
l.onmouseover = lemming_target_on;
l.onmouseout = lemming_target_off;
l.onmousedown = lemming_target_sel;
..
}
At any moment, each lemming is performing a single action, such as walking or climbing, and the action is tracked by a property of Lemming, ani. When the game begins, the lemming falls from a trapdoor, so ani is always initialised to fall:
function Lemming(i,top,left) {
this.ani = 'fall';
...
}
Each type of action requires unique animation and movement. A periodic timer ticks every 60 milliseconds, and updates the game's state, including each lemmings' appearance and positioning. Here's how the animation works. Each action has a collection of 32x32-pixel icons associated with it. The icons are all retained in a single image, with the icons strung together horizontally. The lemming's backgroundImage style is always set to the entire image associated with its current action (Figure 1.67, “DHTML Lemmings - Background Image Used in Floating Sequence”). Because the lemming image is set to 32 pixels, with overflow hidden, you will only ever see one of the icons. To select the appropriate icon, the backgroundPosition property is set. Thus, the backgroundImage remains fixed while the lemming is performing some action, but the backgroundPosition flows rapidly through each of the icons, decrementing by 32 pixels each time until it reaches zero, then back round again.
Whenever the action changes, the script sets the image and initialises the position. Then, for each "tick" of the timer, the icon is advanced by altering the position of the reference image:
Lemming.prototype.changeAnimation = function(ani) {
...
this.pos.backgroundImage = lem[ani][this.dir];
this.pos.backgroundPosition = '0px';
...
}
function lemming_animate(i) {
if (l.curleft == l.maxleft) l.curleft = 0;
else l.curleft -= 32;
if (ie) l.imgpos.left = l.curleft+'px';
else l.pos.backgroundPosition = l.curleft+'px';
}
Why is the appearance changed using backgroundPosition? Wouldn't it be simpler to just run through a sequence of separate image files, and alter the backgroundImage instead? Tino explains the reason is performance: The image is held in memory, whereas image-swapping would require images to be retrieved from the cache.
As for motion, each "tick" delegates to a Strategy function (Gamma et. al, 1995) for the action taking place. The Strategy function then directly manipulates the Sprite's position. So to model a lemming falling vertically, the div's top increases with each tick:
function lemming_fall_ani(l) {
...
l.top += 4;
...
}
Likewise, a walking action involves a change to the left property:
function lemming_walk_ani(l) {
l.left += l.dx;
}
Tiling is a technique in which a big image is built up from small tiles. In fact, Google Maps works this way - each map image is actually a grid of smaller image files. The benefit is that the browser will cache each tile in memory. So if you pan a little in one direction, only the new tiles need to be downloaded. Like Sprites, tiling is a technique you can use to change visual content without extracting everything from the server. And Google Maps shows the two techniques combine effectively.
“Popup” is a close cousin of Sprite. Both share the appearance of being "in front of" the rest of the document, typically implemented with the zIndex style. But they differ semantically. The Sprite pattern is intended to cover small objects, often draggable, animated, and icon-like. Often, Sprites are a standard fixture of the application, have a sense of unique identity, and remain present throughout the application. “Popup” is more about a transient, informational, block of content which appears to show something or accept some input, then vanishes again. “Popup”s tend to be larger, more text-based, and often dynamic in content.
Sprites can often be dragged around a space.
Think of a 20th century cartoonist creating a mouse's walking sequence onto a series of transparent "cel" sheets, to eventually be overlayed on a background scene.