Sprite - Ajax Patterns

Sprite

From Ajax Patterns

(Difference between revisions)
Revision as of 06:19, 23 October 2007
80.91.172.205 (Talk | contribs)
In A Blink
← Previous diff
Current revision
41.135.64.5 (Talk | contribs)
In A Blink - undo damagae from rev 8248
Line 7: Line 7:
= In A Blink = = In A Blink =
-Stick-figure like images over an+Stick-figure like images over an image.
<!-- =================================================================== --> <!-- =================================================================== -->
Line 90: Line 90:
The chat is supported by avatars, [[Sprite]]s representing the users. 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 avatar). Typing in the input box animates the avatar image, and clicking <tt>Enter</tt> submits the mssage for all to see. The user is free to drag the avatars around the page. So the appearance is several avatars moving around and talking to each other, with the web page as a backdrop. The chat is supported by avatars, [[Sprite]]s representing the users. 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 avatar). Typing in the input box animates the avatar image, and clicking <tt>Enter</tt> submits the mssage for all to see. The user is free to drag the avatars around the page. So the appearance is several avatars moving around and talking to each other, with the web page as a backdrop.
 +
 +== WMD Tank Battle ==
 +[http://wmdtb.com WMD Tank Battle] is another example of [[Sprites]] used in the gaming sense. The technique is used for map display and simple animation in a persistent world.
<!-- -------------------------------------------------------------------- --> <!-- -------------------------------------------------------------------- -->
Line 207: Line 210:
* [http://www.askapache.com/css/speedy-sites-with-image-sprites-and-css.html Speedier Sites use CSS Sprites] * [http://www.askapache.com/css/speedy-sites-with-image-sprites-and-css.html Speedier Sites use CSS Sprites]
* [http://www.libpng.org/pub/png/pngintro.html PNG File Format] * [http://www.libpng.org/pub/png/pngintro.html PNG File Format]
- +* [http://www.jennifersemtner.com/ Extending CSS Spriting] Small extension of this pattern to support alt text, IE PNG transparency fix, printouts, and allows image to be declared on the page itself.
-<!-- =================================================================== -->+
= Acknowledgements = = Acknowledgements =
Thanks to Tino Zijdel ("Crisp") for providing some background information on DHTML Lemmings. Thanks to Tino Zijdel ("Crisp") for providing some background information on DHTML Lemmings.

Current revision

Evidence: 2/3

Tags: Bitmap Buffer Character Layer Overlay Shape Sprite Z-Index


Contents

In A Blink

Stick-figure like images over an image.


Goal Story

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 Chainsaw" sprite from the tool palette to the "Security Features" requirement, shakes the mouse a couple of times, and the chainsaw is animated for a second as the requirement fades away.


Problem

How can you ensure visual content is flexible?


Forces

  • 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.


Solution

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 recomputed 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. There are several ways to achieve this effect:

  • Prepare the entire image in advance. But what if you had typed "Restaurant" instead? Or "742 Evergreen Terrace"? What if you'd zoomed in a bit? Google would have to pre-store a new image for each possible search term, an impossibility even for Google.
  • Prepare the entire image on demand. The search might result in a new image being placed on the page, with a URL like http://maps.google.com/image/museum+new+york, which will trigger the browser to load up the new image. When the server detects this request, it could generate the image on demand. This solution solves the infinite storage problem, but is grossly inefficient. Google's servers will be working overtime to pump out new images, and the user will suffer from a slow response time.
  • Download the basic map image, along with semantic content indicating the map's coordinates and those of the museums. The browser will then render the whole image, slapping on a thumbtack sprite for each museum.

The sprite solution is the most scaleable in terms of storage, server processing, and user response times. It makes the map a backdrop for whatever arbitrary data needs to be shown. The sprites are completely flexible. Google could use them, for example, to render a personal map showing places of interest to each individual user. All that needs to be downloaded is some semantic content, and the browser handles the rendering.

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:

  • 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 be set as relative or absolute 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 Event Scheduling. A simple technique is to set up a recurring "changeImage()" action, in which the element's background-image 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 involving code like "var sprite[i] = new Image(); sprite[i].src = "sprite.gif". An alternative animation technique is outlined in the Code Examples below.

Decisions

What file format will the sprite image be?

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: cartoon-like 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 note that the patent in question is now expired, and is no longer a consideration. PNG has some technical advantages over GIF, particularly variable transparency, but unfortunately that's not been correctly implemented in IE until IE7. Fortunately, beginning with IE5.5, it's possible to implement the correct behaviour of png transparency with IE-specific image filters - there are ready-to-use solutions, like PNG Behaviour. There are still some special situations where PNGs cause problems with IE, so the decision can not be made generally. It should be noted though, that these filters are usually only needed to implement transparent PNGs with an alpha channel, one bit transparency (like gif) should work out of the box.

Will the sprite be animated?

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.


Real-World Examples

DHTML Lemmings

DTML 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.

Google Maps

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

Quek is a fun use of sprites as chat avatars. 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 downloads or plugins, but Quek aims for a completely Ajaxian experience. 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.

The chat is supported by avatars, Sprites representing the users. 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 avatar). Typing in the input box animates the avatar image, and clicking Enter submits the mssage for all to see. The user is free to drag the avatars around the page. So the appearance is several avatars moving around and talking to each other, with the web page as a backdrop.

WMD Tank Battle

WMD Tank Battle is another example of Sprites used in the gaming sense. The technique is used for map display and simple animation in a persistent world.


Code Examples

DHTML Lemmings

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 well-known bug where IE 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 class, 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 "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 time, 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. 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.

lemmingfloatr2lb.gif

A single image depicting the entire floating sequence.To depict an activity, the sprite's backgroundImage is fixed to one image, while backgroundPosition continuously cycles through each icon in the sequence.

 

Whenever the action changes, the script sets the image and initalises the position. Then, for each "tick" of the timer, the icon is rotated 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 backgroundPositon? Wouldn't it be simpler to just run through a sequence of separate image files, and alter the backgroundImage instead? Via email, Tino explained 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 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;
 }


Alternatives

Tiling

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.


Related Patterns

Popup

Popup is a close cousin of Sprite, as they are defined here, and some examples arguably fit both patterns. 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, often 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. Popups tend to be larger, more text-based, and often dynamic in content.

Drag-And-Drop

Sprites can often be dragged around a space.


Visual Metaphor

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.


Want to Know More?

Acknowledgements

Thanks to Tino Zijdel ("Crisp") for providing some background information on DHTML Lemmings.