16.2. One-Second MutationAuto-Update, Sync, Sychronize, Real-Time Figure 16-4. One-Second Mutation16.2.1. Goal StoryDoc monitors a patient's condition, watching some histograms shrink and grow to reflect changes in the patient's body. 16.2.2. ProblemThere's a lot of activity in an Ajax App; how can you help the user digest it all? 16.2.3. Forces
16.2.4. SolutionWhen a page element undergoes a value change or some other significant event, dynamically mutate its shape for about a second. In most cases, the mutation is used to reflect a permanent change. Instead of flipping from one form to another, the element gradually transitions into another form. The effect serves a few purposes:
Mutation combines Display Morphing with Scheduling. A loop runs while the second of mutation progresses, with each transition occurring perhaps once every 50 milliseconds. Each transition will likely involve a change to at least one of the following CSS properties:
The first category of mutation involves making an object appear. The following are effective ways to communicate that the object has just been created or retrieved from the server.
The next category of mutation involves some common disappearing actsthe opposite of the effects aboveand a special effect for disappearing:
There are also a few mutations that involve changing from one thing to another:
Most of these effects can be combined with One-Second Spotlight to give a 3-D effect. For example, an item can Materialize and Form at the same time. The human visual system uses both ambience and size as cues for distance, so the effect is that the item is reaching outward from behind the screen toward the user. Note that you probably won't have to hand code effects, because libraries like Scriptaculous (discussed in the following "Real-World Examples") are quite flexible and are usually easy to incorporate into typical scripts. 16.2.5. DecisionsMost of the decisions in One-Second Spotlight are relevant here too. As with One-Second Spotlight, the "One-Second" part of the name is only a guideline. 16.2.6. Real-World Examples16.2.6.1. TiddlyWikiJeremy Ruston's TiddlyWiki (http://tiddlywiki.com), like many of its spin-offs, uses a Grow effect to introduce new content when you click on a Microlink (Chapter 15). The Microlink itself is the starting point. The new content block appears to leap out and, as it flows outward, grows into a full Malleable Content (Chapter 15) block (Figure 16-5). Actually, there are a few visual effects at work here:
There are a few possible variations depending on whether the content is already open and where it's placed. Figure 16-5. TiddlyWiki motion-mutation sequence16.2.6.2. Scriptaculous libraryAs mentioned in One-Second Spotlight, Scriptaculous is a general-purpose JavaScript library. Its visual effects demo (http://script.aculo.us/visual-effects) provides many of the mutation effects described here. 16.2.6.3. DHTML LemmingsDHTML Lemmings is a full-featured implementation of the Lemmings PC game, which uses DOM manipulation to show the lemming creatures performing typical lemming-like activities, such as walking, digging, and clutching an umbrella to make a safe descent from the top of a cliff. As the characters move about, their appearance is animated using quick mutations. While the usage is somewhat different from the usual type of mutation, which tends to focus on highlighting particular pieces of information, it is still of particular interest because it involves manipulation of images instead of just CSS style. See the code walkthrough in Sprite (Chapter 15) for more details. 16.2.7. Code Example: TiddlyWikiLet's look at how TiddlyWiki shows the Grow effect discussed earlier. In doing so, we'll see how its generic animation engine works. TiddlyWiki delegates animation to an Animator, which is capable of animating according to a list of Strategy (Gamma et al., 1995) objects, or animations. Each encapsulates the strategy for a particular animation. At this time, there are just a couple of animation objects: a Zoomer and a Slider. var anim = new Animator( ); function Animator( ) { ... this.animations = []; // List of animations in progress return this; } When a Microlink is clicked, the Animator is passed a Zoomer strategy, which is injected with the Microlink DOM object from which the content will leap out (src), a string title of the Malleable Content (title), the Malleable Content element in which the content will end up (theTiddler), and a flag indicating if the animation should be slow or not (I assume this is for debugging, and you can activate it by holding down Shift or Alt as you click on the link): anim.startAnimating(new Zoomer(title,src,theTiddler,slowly)); startAnimating( ) pushes the new animation onto the list of running animations. If it's not already running, it sets up an animation loop: Animator.prototype.startAnimating = function(animation) { this.animations.push(animation); if(this.running++ == 0) { var me = this; this.timerID = window.setInterval(function( ) {me.doAnimate(me);},25); } } The loop runs every 25 milliseconds. It calls a tick( ) function on each running animation. By analyzing Zoomer's tick( ), you can see how the One-Second Mutation is achieved. Upon construction, Zoomer has already set up a bunch of variables to support this function, as well as an element (element) to be shown during the transition. The variables hold position information about the start and end elements. tick( ), then, is left with the task of interpolation: "if the item starts at point A and ends at point B, how should it look at a particular point in the journey?" The Animator provides a value f between 0 and 1 to define how far along the animation the object is, which makes things much easier. So, if the value is 0.5, the item's width will be halfway between the original width and the item's final width (Figure 16-6). The same idea applies for height and position: this.element.style.left = this.startLeft + (this.targetLeft-this.startLeft) * f + "px"; this.element.style.top = this.startTop + (this.targetTop-this.startTop) * f + "px"; this.element.style.width = this.startWidth + (this.targetWidth-this.startWidth) * f + "px"; this.element.style.height = this.startHeight + (this.targetHeight-this.startHeight) * f + "px"; Figure 16-6. Growth factorA One-Second Spotlight effect is also used here. The target objectthe place the object is "leaping" towardgradually fades in by way of the opacity property (and filter, for compatibility). Finally, the window is scrolled to show the target object: this.targetElement.style.opacity = this.progress; this.targetElement.style.filter = "alpha(opacity:" + this.progress * 100 + ")"; window.scrollTo(0,this.startScroll + (this.targetScroll-this.startScroll) * f); The purpose of mutations is generally to provide a smooth transition from one state to another. Therefore, the final state should generally be reached simply by running the animation for the right period. No special handling needs to take place at the end, right? Well, in practice, there are rounding errors and approximations that make it good practice to explicitly set the final state. Thus, the Zoomer completes the animation by ensuring that the precise opacity is precisely 1 (in fact, the filter should ideally be set here too as a further security against rounding errors). The function also deletes the transient element it created to leap from source to target: Zoomer.prototype.stop = function( ) { this.element.parentNode.removeChild(this.element); this.targetElement.style.opacity = 1; window.scrollTo(0,this.targetScroll); } 16.2.8. Alternatives16.2.8.1. One-Second Spotlight and One-Second MotionOne-Second Spotlight (see earlier) and One-Second Motion (see the next pattern) are also used to draw attention to a significant event. One-Second Mutation is particularly suited for indicating that certain types of changes, such as object creation and removal, have occurred. It is not as well suited for general-purpose attention grabbing. 16.2.9. Related Patterns16.2.9.1. SpriteSprites (Chapter 15) are often changed in rapid succession to give the impression of a smooth transition. 16.2.10. MetaphorMany of the individual effects have their own metaphorsthat's why they're used! Slide Out looks like a physical object sliding outward, Grow looks like an object growing, and so on. 16.2.11. AcknowledgmentsMany of the effects discussed in the preceding Solution are based on an analysis of the Scriptaculous implementation (http://script.aculo.us). Jeremy Ruston's TiddlyWiki code was also instructive, and Jeremy helped clarify part of the code. |