Purposeful Animations - Microsoft Press Programming Windows Store Apps with HTML, CSS and JavaScript (2014)

Microsoft Press Programming Windows Store Apps with HTML, CSS and JavaScript (2014)

Chapter 14 Purposeful Animations

In the early 1990s, the wonderful world of multimedia first became prevalent on Windows PCs. Before that time it was difficult for such machines to play audio and video, access compact discs (remember those?), and otherwise provide the rich experience we take for granted today. The multimedia experience was new and exciting, and many people jumped in wholeheartedly, including the group of developer support engineers at Microsoft specializing in this area. Though my team (specializing in UI) sat more than 100 feet away from their area, we could clearly hear—for most of the day!—the various chirps and bleeps emitting from their speakers, against the background of a soft Amazon basin rainfall.

At that time too, many consumers of Windows were having fun attaching all kinds of crazy sounds to every mouse click, window transition, email arrival, and every other system event they could think of. Yet after a month or two of this sensual overload—not unlike being at a busy carnival—most people started to remove quite a few of those sounds, if not disable them altogether. I, for one, eventually turned off all my sounds. Simply said, I got tired of the extra noise.

Along these same lines, you may remember that when DVDs first appeared in their full glory, just about every title had fancy menus with clever transitions. No more: most consumers, I think, got tired of waiting for all this to happen and just want to get on with the business of watching the movie as quickly as possible.

Today we’re reliving this same experience with fluid animations. Now that most systems have highly responsive touch screens and GPUs capable of providing very smooth graphical transitions, it’s tempting to use animations superfluously. However, unless the animations actually add meaning and function to an app, consumers will likely tire of them like they did with DVD menus, especially if they end up interfering with a workflow by making one constantly wait for the animations to finish. I’ll bet that every reader of this book has, at least once, repeatedly hit the Menu button on a DVD remote to no avail….

This is why Windows Store app design speaks of purposeful animations: if there’s no real purpose behind an animation in your app, ask yourself, “Why am I wanting to use this?” Take a moment, in fact, to use Windows and some of the built-in apps to explore how animations are both used and not used. Notice how many animations are specifically to track or otherwise give immediate feedback for touch interactions, which purposefully help users know that their input is being registered by the system. Other animations, such as when items are added or removed from a list, are intended to draw attention to the change, soften its visual impact, and give it a sense of fluidity. In other cases, you may find apps that overdo animations, simply using them because they’re available or trying too hard to emulate physical motion where it’s simply not necessary. In this way, excessive animations constitute a kind of “chrome” with the same effect as other chrome: distracting the user from the content they really care about. (If you can’t resist the temptation to add little effects that are like this, consider at least providing a setting to turn them off.)

Purposeful animations serve the needs of people rather than just showing off technology. Let me put it another way. When thinking about animations, ask yourself, “What do they communicate?” Animations are a form of communication, a kind of visual language. I would even venture to say (as I am venturing now) that animations really only say one or a combination of three things:

• “Thanks, I heard you,” as when something on the screen moves naturally in response to a user gesture. Without this communication, the user might think that their gesture didn’t register and will almost certainly poke at the app again.

• “Hello” and “Goodbye,” as when items appear or disappear from view, or transition one to another. Without this communication, changes that happen to on-screen elements can be as jarring as Bilbo Baggins in Lord of the Rings slipping on the Ring of Power and instantly vanishing. This is not to say that most consumers are incredulous hobbits, of course, but you get the idea.

• “Hey, look at me!” as when something moves to only gain attention or look cute.

If I were to assign percentages to these categories to represent how often they would or should be used, I’d probably put them at 80%, 15%, and 5%, respectively (although some animations will serve multiple purposes). Put another way, the first bit of communication is really about listening and responding, which is what an app should be doing most of the time. The second bit is about courtesy, which is another good quality to express within reason—courtesy can, like handshakes, hugs, bows, and salutes, be overused to the point of annoyance. The third bit, for its part, can be helpful when there’s a real and sincere reason to raise your hand or offer a friendly wave, but otherwise can easily become just another means of showing off.

There’s another good reason to be judicious about the use of animations and really make them count: power consumption. No matter how it’s accomplished, via GPU or CPU, animation is an expensive process. Every watt of juice in a consumer’s batteries should be directed toward fulfilling their goals with their device rather than scattered to the wind. Again, this is why this chapter is called “Purposeful Animations” and not just “Animations”!

In any case, you and your designers are the ultimate arbiters of how and when you’ll use animations. Let me emphasize here that animations should be part of an app’s design, not just an implementation detail. Animations are very much part of the overall user experience of an app. Oftentimes app designs focus on static wireframes and static mockups, neither of which indicate dynamic elements like animations and transitions. Animations are also tightly coupled to the app’s layout and should be designed alongside that layout from the earliest stages of design.

In this uncommonly short chapter, then, we’ll first look at what’s provided for you in the WinJS Animations Library, a collection of animations built on CSS that already embody the Windows look and feel for many common operations. After that we’ll review the underlying CSS capabilities that you can, of course, use directly. In fact, aside from games and other apps whose primary content consists of animated objects, you can probably use CSS for most other animation needs. This is a good idea because the CSS engine is very much optimized to take advantage of hardware acceleration, something that isn’t true when doing frame-by-frame animations in JavaScript yourself. Nevertheless, we’ll end this chapter on that latter subject, as there are some tips and tricks for doing it well within Windows Store apps.

Systemwide Enabling and Disabling of Animations

Before we go any further, take a moment to check PC Settings > Ease of Access > Other Options > Play Animations in Windows and make sure that it's turned on (see Figure 14-1). If it's off, you won't be seeing many animations in the system, including those that you trigger through WinJS APIs. This can be very confusing when writing an app that uses animations!

images

FIGURE 14-1 A very important setting for animation in the desktop control panel.

The idea behind this check box is that for some users, animations are a real distraction that can make the entire machine more difficult to use. For medical reasons too, some users might elect to minimize on-screen movement just to keep the whole experience more calm. So when this option is checked, the WinJS animations don’t actually do anything, and it’s recommended that apps also disable many if not all of their own custom animations as well.

The setting value is obtained through the Windows.UI.ViewManagement.UISettings class in its animationsEnabled property:

var settings = new Windows.UI.ViewManagement.UISettings();
var enabled = settings.animationsEnabled;

You can also just call the WinJS.UI.isAnimationEnabled method that will return true or false depending on this property. WinJS uses this internally to manage its own animation behavior.

WinJS also adds an enablement count that you can use to temporarily enable or disable animations in conjunction with the animationsEnabled value. You change this count by calling WinJS.UI.-enableAnimations and WinJS.UI.disableAnimations, the effects of which are cumulative, and the animationsEnabled property counts as 0 if the PC Settings option is off and 1 if it’s on.

When implementing your own animations either with CSS or with mechanisms like setInterval or requestAnimationFrame, be sensitive to the animationsEnabled setting where appropriate. I add this condition because if an animation is essential to the actual content of an app, like a game, then it’s not appropriate to apply this setting. The same goes for animating something like a clock within a clock app. It’s really about animations that add a fast-and-fluid effect to the content but can be turned off without ill effect.

The WinJS Animations Library

When considering animations for your app, the first place you should turn is the Animations Library in WinJS, found in the WinJS.UI.Animation namespace. Each animation is a function within this namespace that you call when you want a certain kind of animation or transition to happen. The benefit of using these is that they directly embody the Windows look and feel and, in fact, are what WinJS itself uses to animate its own controls, flyouts, and so forth to match the user interface design guidelines. What’s more, because they are built with CSS transitions and animations, they aren’t dependent on WinRT and are fully functional within web context pages that have loaded WinJS (but they do again pay attention to whether animations are enabled as described in the previous section).

All of the animations, as listed in the table below, have guidance as to when and how they should be applied. These are again really design questions more than implementation questions, as stated earlier. By being aware of what’s in the animations library, designers can more readily see where animations are appropriately applied and include them early on in their app design, which makes your life as a developer all the more predictable.

You can find full guidance in Animating Your UI and its subtopics in the documentation, which will also contain specific guidelines for the individual animations below. I will only summarize here.

Key point Built-in controls and other UI elements like those we’ve worked with in previous chapters already make use of the appropriate animations. For example, you don’t need to animate a button tap in the button element nor animate the appearance or disappearance of controls likeWinJS.UI.-Appbar. You’ll primarily use them when implementing UI directly with HTML layout or when building custom controls.

images

images

If you want to see what these animations are actually doing, you can find all of that in the WinJS source code’s ui.js file. Just search for the method, and you’ll see how they’re set up. The Crossfade animation, for example, animates the incoming element’s opacity property from 0 to 1 over 167ms with a linear timing function, while animating the outgoing element’s opacity from 1 to 0 in the same way. The Pointer Down animation changes the element’s scale from 100% to 97.5% over 167ms according to a cubic-bezier curve, while Pointer Up does the opposite.

Knowing these characteristics—known as animation metrics—can be important when creating web-based content to integrate with your app. Because you cannot presently use WinJS on a remote web page, knowing how these animations work will help you emulate that behavior on such pages.

Within your app, though, avoid hard-coding the metrics. Instead, acquire them programmatically through the API in Windows.Core.UI.AnimationMetrics. You can find a demonstration of using this API in the Animation metrics sample.

As interesting as such details might be, of course, they are always subject to change (hence the API). And in the end, what’s important is that you choose animations not for their visual effects but for their semantic meaning, using the right animations at the right times in the right places. So let’s see how we do that.

Tip #1 All of the WinJS animations are implemented using the WinJS.UI.executeAnimation and WinJS.UI.executeTransition functions, which you can use for custom animations as well.

Tip #2 While an animation is running, always avoid changing an element’s contents and its CSS styles that affect the same properties. The results are unpredictable and unreliable and can cause performance problems.

Animations in Action

To see all of the WinJS animations in action, run the HTML animation library sample. There are many different animations to illustrate, and this sample most certainly earns the award for the largest number of scenarios: twenty-two! In fact, the first thing you should do is go to scenario 22 and see whether animations are enabled, as that will most certainly affect your experience with the rest of the sample. The output of that scenario will show you whether the UISettings.animationsEnabled flag is set and allow you to increment or decrement the WinJS enablement count. So go check that now, because if you’re like me (I dislike waiting for my task bar to animate up and down), you might have turned off system animations a long time ago for a snappier desktop experience. I didn’t realize at first that it affected WinJS in this way!

Clearly, with 22 scenarios in the sample I won’t be showing code for all of them here; indeed, doing so isn’t necessary because many operate in the same way. The only real distinction is between those whose methods start with create and those that don’t, as we’ll see in a bit.

All the animation methods return a promise that you can use to take additional action when the animation is complete (at which point the completed handlers given to then/done will be called). If you already know something about CSS transitions and animations, you’ll rightly guess that these promises encapsulate events like transitionend and animationend, so you won’t need to listen for those events directly if you want to chain or synchronize animations. For chaining, you can just chain the promises; for synchronization, you can obtain the promises for multiple animations and wait for their completion using methods like WinJS.Promise.join or WinJS.Promise.any.

Animation promises also support the cancel method, which removes the animation from the element. This immediately sets the affected property values to their final states, causing an immediate visual jump to that end state. And whether you cancel an animation or it ends on its own, the promise is considered to have completed successfully; canceling an animation, in other words, will call the promise’s completed handler and not its error handler.

Be aware that because all of the WinJS animations are implemented with CSS, they won’t actually start until you give control back to the UI thread. This means that you can set up multiple animations knowing that they’ll more or less start together once you return from the function. So even though the animation methods return promises, they are not like other asynchronous operations in WinRT that start running on another thread altogether.

Anyway, let’s look at some code! In the simplest case, all you need to do is call one of the animation methods and the animation will execute when you yield. Scenario 6 of the sample, for instance, just adds these handlers to the pointerdown and pointerup events of three different elements (js/pointerFeedback.js):

function onPointerDown(evt) {
   WinJS.UI.Animation.pointerDown(evt.srcElement);
}
 
function onPointerUp(evt) {
   WinJS.UI.Animation.pointerUp(evt.srcElement);
}

We typically don’t need to do anything when the animations complete, so there’s no need for us to call done or provide a completed handler. Truly, using many of these animations is just this simple.

The crossFade animation, for its part (scenario 10), takes two elements: the incoming element and the outgoing element (all of which must be visible and part of the DOM throughout the animation, mind you!). Calling it then looks like this (js/crossfade.js):

WinJS.UI.Animation.crossFade(incoming, outgoing);

Yet this isn’t the whole story. A common feature among the animations is that you can provide an array of elements on which to execute the same animation or, in the case of crossFade, two arrays of elements. While this isn’t useful for animations like pointerDown and pointerUp (each pointer event should be handled independently), it’s certainly handy for most others.

Consider the enterPage animation. In its singular form it accepts an element to animate and an optional initial offset where the element begins relative to its final position. (Generally speaking, you should omit this offset if you don’t need it, because it will result in better performance—the sample passes null here, which I’ve omitted in the code below.) enterPage can also accept a collection of elements, such as the result of a querySelectorAll. Scenario 1 (html/enterPage.html and js/enterPage.js) provides a choice of how many elements are animated separately:

switch (pageSections) {
   case"1":
      // Animate the whole page together
      enterPage = WinJS.UI.Animation.enterPage(rootGrid);
      break;
   case"2":
      // Stagger the header and body
      enterPage = WinJS.UI.Animation.enterPage([[header, featureLabel], [contentHost,
         footer]]);
      break;
   case"3":
      // Stagger the header, input, and output areas
      enterPage = WinJS.UI.Animation.enterPage([[header, featureLabel],
      [inputLabel, input],[outputLabel, output, footer]]);
      break;
}

When the element argument is an array, the offset argument, if provided, can be either a single offset that is applied to all elements, or an array to indicate individual offsets for each element. Each offset is an object whose properties define the offset. See js/dragBetween.js for scenario 13 where this is used with the dragBetweenEnter animation:

WinJS.UI.Animation.dragBetweenEnter([box1, box2],
   [{ top: "-40px", left: "0px" }, { top: "40px", left: "0px" }]);

Here’s a modification showing a single offset that’s applied to both elements:

WinJS.UI.Animation.dragBetweenEnter([box1, box2],{ top: "0px", left: "40px" });

Scenario 4 (js/transitioncontent.js) shows how you can chain a couple of promises together to transition between two different blocks of content:102

WinJS.UI.Animation.exitContent(outgoing, null).done( function () {
   outgoing.style.display = "none";
   incoming.style.display = "block";
   return WinJS.UI.Animation.enterContent(incoming, null);
});

Things get a little more interesting when we look at the create* animation methods, together referred to as the layout animations, which are for adding and removing items from lists, expanding and collapsing content, and so forth. Each of these has a three-step process where you (1) create the animation, (2) manipulate the DOM, and then (3) execute the animation, as shown in scenario 7 (js/addAndDeleteFromList.js):

// Create addToList animation.
var addToList = WinJS.UI.Animation.createAddToListAnimation(newItem, affectedItems);
 
// Insert new item into DOM tree.This causes the affected items to change position.
list.insertBefore(newItem, list.firstChild);
 
// Execute the animation.
addToList.execute();

The reason for the three-step process is that in order to carry out the animation on newly added items or items that are being removed, they all need to be in the DOM when the animation executes. The process here lets you create the animation with the initial state of everything, manipulate the DOM (or just set styles and so forth) to create the ending state, and then execute the animation to “let ‘er rip.” You can then use the done method on the promise returned from execute to perform any final cleanup. Scenario 5 (js/expandAndCollapse.js) makes this point clear:

// Create collapse animation.
var collapseAnimation = WinJS.UI.Animation.createCollapseAnimation(element, affected);
 
// Remove collapsing item from document flow so that affected items reflow to their new
// position.Do not remove collapsing item from DOM or display at this point, otherwise the
// animation on the collapsing item will not display.
element.style.position = "absolute";
element.style.opacity = "0";
 
// Execute collapse animation.
collapseAnimation.execute().done(
   // After animation is complete (or on error), remove from display.
   function () { element.style.display = "none"; },
   function () { element.style.display = "none"; }
);

As a final example—because I know you’re smart enough to look at most of the other cases on your own—scenario 21 (js/customAnimation.js) shows how to use the WinJS.UI.executeAnimation and WinJS.UI.executeTransition methods.

function runCustomShowAnimation() {
var showAnimation = WinJS.UI.executeAnimation(
       target,
       {
          // Note: this keyframe refers to a keyframe defined in customAnimation.css.
          // If it's not defined in CSS, the animation won't work.
          keyframe: "custom-opacity-in",
          property: "opacity",
          delay: 0,
          duration: 500,
          timing: "linear",
          from: 0,
          to: 1
       }
   );
}
 
function runCustomShowTransition() {
   var showTransition = WinJS.UI.executeTransition(
       target,
       {
          property: "opacity",
          delay: 0,
          duration: 500,
          timing: "linear",
          to: 1
       }
   );
}

If you want to combine multiple animations (as many of the WinJS animations do), note that both of these functions return promises so that you can combine multiple results with WinJS.Promise.join and have a single completed handler in which to do post-processing. This is exactly what WinJS does internally.

And if you know anything about CSS animations and transitions already, you can probably tell that the objects you pass to executeAnimation and executeTransition are simply shorthand expressions of the CSS styles you would use otherwise. In short, these methods give you an easy way to set up your own custom animations and transitions through the capabilities of CSS. Let’s now look at those capabilities directly.

Sidebar: Parallax/Panorama Animations

Developers often ask how to create a parallax or panorama background animation as seen on the Windows Start screen. If you’re not familiar with this concept, go to the Start screen and pan around a little, noticing how the background pans as well but slower than the tiles. This creates a sense of the tiles floating above the background.

While it is possible to implement this effect in JavaScript (see the KidsBook example on the Internet Explorer TestDrive site), we don’t recommend it or at least recommend providing a setting to turn the effect off. At issue is the fact that such animations run dependently (on the CPU) rather than independently (on the GPU), as described in the next section. As such, the threading and rendering model of JavaScript results in choppy movement except on high-power devices; the effect will be very pronounced on low-power and especially ARM devices. In addition, such animations can be costly in terms of CPU and battery utilization.

This is one case in which using C++ and DirectX (or even C#/VB and XAML) has a clear advantage over JavaScript, and would be a consideration if you absolutely must have this effect in your app.

CSS Animations and Transitions

As noted before, many animation needs can be achieved through CSS rather than with JavaScript code running on intervals or animation frames. The WinJS Animations Library, as we’ve just seen, is entirely built on CSS. Using CSS relieves us from writing a bunch of code that worries about how much to move every element in every frame based on elapsed time and synchronized to the refresh rate. Instead, we can simply declare what we want to happen (perhaps using the WinJS.UI.executeAnimation and WinJS.UI.executeTransitionhelpers as shown earlier in "Animations in Action") and let the app host take care of the details. Delegation at its best! In this section, then, let’s take a closer look at the capabilities of CSS for Windows Store apps.

Another huge benefit of performing animations and transitions through CSS—specifically those that affect only transform and opacity properties—is that they can be used to create what are called independent animations that run on a GPU thread rather than the UI thread. This makes them smoother and more power-efficient than dependent animations that are using the UI thread. Dependent animations happen when you create animations in JavaScript using intervals, use CSS animations and transitions with properties other than transform and opacity, or run animations on elements that are partly or wholly obscured by other elements. (If you want to get into the details, refer to Independent Composition: Rendering and Compositing in Internet Explorer 10, which applies to Windows Store apps written in JavaScript.)

We’ll come back to this subject in a bit when we look at sample code. Let’s first review how CSS animations and transitions work (and you can find many more tutorials on the web). I say both animations and transitions because there are, in fact, two separate CSS specifications: CSS animationsand CSS transitions. So what’s the difference?

Normally when a CSS property changes, its value jumps immediately from the old value to the new value, resulting in a sudden visual change. Transitions instruct the app host how to change one or more property values gradually, according to specific delay, duration, and timing curve parameters. All of this is declared within a specific style rule for an element (as well as :before and :after pseudo-elements) using four individual styles:

transition-property (transitionProperty in JavaScript) Identifies the CSS properties affected by the transition (the transitionable properties are listed in section 7 of the transitions spec).

transition-duration (transitionDuration in JavaScript) Defines the duration of the transition in seconds (fractional seconds are supported, as in .125s; negative values are normalized to 0s).

transition-delay (transitionDelay in JavaScript) Defines the delayed start of the transitions relative to the moment the property is changed, in seconds. If a negative value is given, the transition will appear to have started earlier but the effect will not have been visible.

transition-timing-function (transitionTimingFunction in JavaScript) Defines how the property values change over time. The functions are ease,linear,ease-in,ease-out,ease-in-out, cubic-bezier, step-start, and step-end. The W3C spec has some helpful diagrams that explain these, but the best way to see the difference is to try them out in running code.

For example, a transition for a single property appears like so:

#div1 {
   transition-property: left;
   transition-duration: 2s;
   transition-delay: .5s;
   transition-timing-function: linear;
}

When defining transitions for multiple properties, each value in each style is separated by a comma:

.class2 {
   transition-property: opacity, left;
   transition-duration: 1s, 0.25s;
}

Again, transitions don’t specify any actual beginning or ending property values—they define how the change actually happens whenever a new property is set through another CSS rule or through JavaScript. So, in the first case above, if left is initially 100px and it’s set to 300px through a :hover rule, it will transition after 0.5 seconds from 100px to 300px over a period of 2 seconds. Doing the math, the visual movement with a linear timing function will run at 100px/second. Other timing functions will show different rates of movement at different points along the 2-second duration.

If a bit of JavaScript then sets the value to -200px—ideally after the first transition completes and fires its transitionend event—the value will again transition over the same amount of time but now from 300px to -200px (a total of 500px). As a result, the element will move at a higher speed (250px/second, again with the linear timing function) because it has more ground to cover for the same transition duration.

What’s also true for transitions is that if you assign a style (e.g., class2 above) to an element, nothing will happen until an affected property changes value. Changing a style like this also has no effect if a transition is already in progress. The exception is if you change the transition-property value, in which case that transition will stop. With this, it’s important to note that the default value of this property is all, so clearing it (setting it to "") doesn’t stop all transitions—it enables them! You instead need to set the property to none.

Note Elements with display:none do not run CSS animations and transitions at all, for obvious reasons. The same cannot be said about elements with display:hidden, visibility:hidden, visibility:collapsed, or opacity:0, which means that hiding elements with some means other thandisplay:none might end up running animations on nonvisible elements, which is a complete waste of resources. In short, use display:none.

Animations work in an opposite manner to transitions. Animations are defined separately from any CSS style rules but are then attached to rules. Assigning that style to an element then triggers the animation immediately applying any delay defined in the animation. (The animation can be initially paused as well if you want to start it at a later time.) Furthermore, groups of affected properties are defined together in keyframes and are thus animated together.

A CSS animation, in other words, is an instruction to progressively update one or more CSS property values over a period of time. The values change from an initial state to a final state through various intermediate states defined by a set of keyframes. Here’s an example (from scenario 1 of the HTML independent animations sample we’ll be referring to later in this chapter):

@keyframesmove {
   from {transform: translateX(0px);}
   50% {transform: translateX(400px);}
   to {transform: translateX(800px);}
}

More generally:

• Start with @keyframes<identifier> where <identifier> is a name for the animation (like move above). You’ll refer to this identifier elsewhere in style rules.

• Within this animation, create any number of individual keyframes (also called rule sets), each of which represents a different snapshot of the animated element at different stages in the overall animation, demarked by percentages. The from and to keywords, as shown above, are simply aliases for 0% and 100%, respectively.

• Within each keyframe, define the desired value of any number of style properties (just transform in the example above), with each separated by a semicolon as with CSS styles. If a value for a property is the same as in the previous rule set, no animation will occur for that property. If the value is different, the rendering engine will animate the change between the two values of that property across the amount of time equivalent to <overall animation time> * (<toPercentage> - <fromPercentage>)/100. A timing function can also be specified for each rule set using theanimation-timing-function style. For example:

    50% { transform: translateX(400px); animation-timing-function: ease-in;}

One thing you’ll notice here is that while the keyframe can indicate a timing function, it doesn’t say anything about actual timings. This is left for the specific style rules that refer to the animation. In scenario 1 of the sample, for instance:

.ball {
   animation-name: move;
   animation-duration: 2s;
   animation-timing-function: linear;
   animation-delay: 0s;
   animation-iteration-count:infinite;
   animation-play-state: running;
}

Here, the animation-name style (animationName in JavaScript) identifies the animation to apply, and we'll look at the other styles in a moment. Before that, it's essential to understand that this ball style class is what gets added to an element to start the move animation according to the other timing properties. Again, the animation begins when that class is assigned. In typical practice, you can define a separate class like this that contains just the animation styles and then add that class to any number of elements individually. Scenario 1 of the sample does exactly this with two different elements, one that runs independently and one that runs dependently (js/scenario1.js):103

IndependentAnimationelement.className = 'ball';
DependentAnimation.className = 'ball';
IndependentAnimationelement.style.animationPlayState = 'running';
DependentAnimation.style.animationPlayState = 'running';

Setting the animationPlayState properties here is not actually necessary because the ball class already sets that. If, however, the style class initially sets paused, setting the play state to running will then start the animation.

Note too that you can also have any number of classes with different animation* styles that all refer to the same animation-name, which allows you to reuse the same animation with any number of timing variations.

In any case, the other animation-* styles describe how the animation should execute:

animation-duration (animationDuration in JavaScript) The duration of the animation in seconds (fractions allowed, as in 0.4s) or milliseconds (as in 400ms). Negatives are the same as 0s.

animation-timing-function (animationTimingFunction in JavaScript) Defines, as with transitions, how the property values are interpolated over time—ease (the default),linear,ease-in,ease-out,ease-in-out, cubic-bezier, step-start, and step-end.

animation-delay (animationDelay in JavaScript) Defines the number of seconds (s suffix) or milliseconds (ms suffix) after which the animation will start when the style is applied. This can be negative, as with transitions, which will start the animation partway through its cycle.

animation-iteration-count (animationIterationCount in JavaScript) Indicates how many times the animation will repeat (default is 1). This can be a number or infinite, as shown above. If you want an animation to run forwards and then backwards, such as creating a temporary swell effect, set this count to 2 and the animation-direction (below) to alternate, as each direction will count as an iteration.

animation-direction (animationDirection in JavaScript) Indicates whether the animation should play normal (forward), reverse, alternate (back and forth), or alternate-reverse (back and forth starting with reverse). The default is normal. Note again that alternate and alternate-reverse require you to have at least two iterations because each direction counts as a separate iteration.

animation-play-state (animationPlayState in JavaScript) Sets the initial run state of the animation: the default state of running plays the animation as soon as the style is set on the element, whereas paused will hold off starting the animation until you set the style to running. You can use this style from JavaScript to start and pause animations whenever needed until the animation has completed. After that time, setting the play state has no effect.

animation-fill-mode (animationFillMode in JavaScript) Defines which property values of the named keyframe will be applied when the animation is not executing, such as during the initial delay or after it is completed. The default value of none applies the values of the 0% or from rule set if the direction is forward and alternate directions; it applies those of the 100% or to rule set if the direction is reverse or alternate-reverse. A fill mode of backwards flips this around. A fill mode of forwards always applies the 100% or to values (unless the iteration count is zero, in which case it acts like backwards). The other option, both, is the same as indicating both forwards and backwards.

animation (animation in JavaScript) The shorthand style for all of the above (except for animation-play-state) in the order of name, duration, timing function, delay, iteration count, direction, and fill mode.

Again, applying a style that contains animation-name will trigger the animation for that element if the play state is running. You can do this by setting an element's className directly, as shown in the code from the HTML independent animations sample shown earlier, but it's better to useWinJS.Utilities.addClass because this will not affect any of the element's existing classes.

Triggering an animation will also happen automatically if the animation is named in a style that’s applied to an element by default in your CSS. You can set the animation property for an element, of course, and if the play state is initially set to paused in CSS, you can then trigger the animation on demand by changing the animationPlayState property to running.

The nature of a CSS animation is to run until it's complete, during which time you can pause and start it through animationPlayState, but once the animation is complete you have to remove the animation styles from an element and then add them again to restart that animation.

This is where the element's animationend event comes into play: it's the right place to remove the animation class from the element, ideally using WinJS.Utilities.removeClass, which lets you remove a specific class from an element without affecting any others. For example, here's how I'd set up a handler for the event to automatically reset the ball animation for an element:

element.addEventListener("animationend", resetBallAnimation);
WinJS.Utilities.addClass(element, "ball");
 
function resetBallAnimation(e) {
   e.target.removeEventListener("animationend", resetBallAnimation);
   WinJS.Utilities.removeClass(e.target, "ball");
}

This way, the next time I add the ball class to the element, the animation will run again.

Keyframes, although typically defined in CSS, can also be defined in JavaScript. The first step is to build up a string that matches what you’d write in CSS, and then you insert that string to the stylesheet. This is shown in scenario 7 of the HTML independent animations sample (js/scenario7.js, which also has a demonstration of the animationend event):

var styleSheet = document.styleSheets[1];
var element1 = document.getElementById("ballcontainer");
var animationString = '@keyframes bounce1 {'
   // ...
   + '}';
 
styleSheet.insertRule(animationString, 0);
 
window.setImmediate(function () {
   element1.style.animationName = 'bounce1';
});

Note how setImmediate is used to yield to the UI thread before setting the animationName property to trigger the animation. This ensures that whatever other code follows (not shown here) will execute first, as it does some other work the sample wants to complete before the animation begins.

More generally, it’s good to again remember that CSS animations and transitions start only when you return from whatever function is setting them up. That is, nothing happens visually until you yield back to the UI thread and the rendering engine kicks in again, just like when you change nonanimated properties. This means you can set up however many animations and transitions as desired, and they’ll all execute simultaneously. Using a callback with setImmediate, as shown above, is a simple way to say, “Run this code as soon as there is no pending work on the UI thread.”104 Such a pattern is typically for triggering one or more animations once everything else is set up.

Of course, you should ideally prioritize your animation work in relation to other work happening on the UI thread, using the WinJS scheduler API as discussed in Chapter 3, "App Anatomy and Performance Fundamentals."

As a final note for this section, you might be interested in The Guide to CSS Animation: Principles and Examples (Smashing Magazine). This will tell you a lot about animation design beyond just how CSS animations are set up in your code.

Designing Animations in Blend for Visual Studio

Although you can define CSS animations directly in text, it helps to have a way to immediately visualize the results and make adjustments. Blend for Visual Studio provides just such a design experience, which I demonstrate in Video 14-1. (Another demonstration can be found in the //build 2013 session 2-311, What's New in Blend for HTML Developers, between time indices 42:00 and 46:00.)

You start in the Live DOM pane by selecting the element to which you want to apply an animation. Under the CSS properties pane, then, expand the Animations group that appears as follows:

images

Click the + in the upper right (circled) to create an animation with whatever name you enter, and then set the animation's timing properties in the drop-downs below. This will create the appropriate animation reference for the element. For example, if I've selected an element with the iddivAnimate and create a simple back-and-forth animation called swell (to briefly enlarge the element, which requires two iterations), the following will be added to the CSS:

#divAnimate {
   animation: swell 300msease-in 0ms2 alternate forwards;
   animation-play-state: running;
}

Next we need to define the animation's keyframes. Click the Create Animation button at the lower right and Blend opens up a timeline view:

images

At this point you're in Recording Mode, which is indicated by the red dot at the left end of the playback controls and by a red dot and frame around the artboard. Here, set the playback cursor to a position where you want to define a keyframe. In the CSS Properties pane you'll see the name of the animation at the top; the list of properties that appear underneath are those that can be animated:

images

Recording Mode means that any properties you set in this CSS properties pane are recorded in the keyframe for the current timeline position. And as long as you're in this mode, you can move the timeline position around and edit more keyframes. For example, if I place the playback position to 0s and set a scaling transform to 1, and then I move the playback position to the end of the first iteration and set a scaling transform to 1.2 plus, I get the following in CSS:

@keyframesswell {
   0% {
      transform: scale(1,1);
   }
 
   100% {
      transform: scale(1.2,1.2);
   }
}

In the animation timeline you can then use the other playback controls to see the result. You can also drag the timeline positioner back and forth. The animation will run in Interactive Mode as well.

When you're done editing, turn recording off by clicking the red dot; you'll see the CSS Properties pane revert to the normal view.

The HTML Independent Animations Sample

Let’s now more fully examine the HTML independent animations sample. Scenario 1 gives a demonstration of an independent versus a dependent animation by eating some time on the UI thread (that is, blocking that thread) according to a slider. As a result, the top red ball (see image below) moves choppily, especially as you increase the work on the UI thread by moving the slider—the dynamic effect is shown in Video 14-2. The green ball on the bottom, on the other hand, continues to move smoothly the whole time.

images

What’s tricky to understand about this sample is that both balls use the same CSS style rule named ball that we saw earlier. In fact, just about everything about the two elements is exactly the same. So why does the movement of the red ball get choppy when additional work is happening on the UI thread?

The secret is in the z-index:-1; style on the red ball in css/scenario1.css (and a corresponding lack of position:static which negates z-index). For animations to run independently, they must be free of obstruction. This really gets into the subject of how layout is being composed within the HTML/CSS rendering engine of the app host—an animating element that’s somewhere in the middle of the z-order might end up being independent or dependent. The short of it is that the z-index style is the only lever that’s available for you to pull here.

As I noted before, independent animations are limited to those that affect only the transform and opacity properties for an element. If you animate any property that affects layout, like width or left, the animation will run as dependent (and similar results can be achieved with a scaling and translation transform anyway). Other factors also affect independent animations, as described on the Best Practices: Animating topic in the documentation. For example, if the system lacks a GPU, if you overload the GPU with too many independent animations, or if the elements are too large, some of the animations will revert to dependent. This is another good reason to be purposeful in your use of animations—overusing them will produce a terrible user experience on lower-end devices, thereby defeating the whole point of using animations to enhance the user experience!

The other scenarios of the HTML independent animations sample let you play with CSS transitions and animations by setting values within various controls and then running the animation. Scenarios 2 and 3 work with CSS transitions for 2D and 3D transforms, respectively, with an effect of the latter shown in Figure 14-2 and both shown more dynamically in Video 14-3. As you can see, the element that the sample animates is the container for all the input controls! Scenarios 5 and 6 then let you do similar things with CSS animations. In all these cases, the necessary styles are set directly in JavaScript rather than using declarative CSS, so look in the .js files and not the .css files for the details.

images

FIGURE 14-2 Output of scenario 3 of the HTML independent animations sample.

Scenarios 4 and 7 show something we've only touched on so far, which are the few simple events that are raised for transitions and animations (and actually have nothing to do with independent versus dependent animations). In the former case, any element on which you execute a CSS transition will fire transitionstart and transitionend events. You can use these to chain transitions together.

With animations, there are three events: animationstart (which comes after any delay has passed), animationend (when the animation finished), and animationiteration (at the end of each iteration, unless animationend also fires at the same time). As with transitions, all of these can be used to chain animations or otherwise synchronize them. The animationiteration event is also helpful if you need to run a little code every time an animation finishes a cycle. In such a handler you might check conditions that would cause you to stop an animation, in which case you can set the animationPlayState to paused when needed.

Rolling Your Own: Tips and Tricks

If you’re anything like me, I imagine that one of the first things you did when you started playing with JavaScript is to do some kind of animation: set up some initial conditions, create a timer with setInterval, do some calculations in the handler and update elements (or draw on a canvas105), and keep looping until you’re done. After all, this sort of thing is at the heart of many of our favorite games! (For an introductory discussion on this, just in case you haven’t done this on your own yet, see How to animate canvas graphics.)

Considerable wisdom on this subject is available in the community if you decide to go this route. I put it this way because by now, having looked at the WinJS animations library and the capabilities of CSS, you should be in a good position to decide whether you actually need to go this route at all. Some people have estimated that a vast majority of animations needed by most apps can be handled entirely through CSS: just set a style and let the app host do the rest.106 But if you decide that you still need to do low-level animation, the first thing you should do is ask yourself this question:

What is the appropriate animation interval?

This is a very important question because oftentimes developers have no idea what kind of interval to use for animation. It’s not so much of an issue for long intervals, like 500ms or 1s, but developers often just use 10ms because it seems “fast enough.”

To be honest, 10ms is overkill for a number of reasons. 60 frames per second (fps)—an animation interval of 16.7ms—is about the best that human beings can even discern and is also the best that most displays can even handle in the first place. In fact, the best results are obtained when your animation frames are synchronized with the screen refresh rate.

Let’s explore this a little more. Have you ever looked at a screen while eating something really crunchy and noticed how the pixels seem to dance all over the place? This is because display devices aren’t typically just passive viewports onto the graphics memory. Instead, displays typically cycle through graphics memory at a set refresh rate, which is most commonly 60Hz or 60fps (but can also be as low as 50Hz or as high as 100Hz).

This means that trying to draw animations at an interval faster than the refresh rate is a waste of time, is a waste of power (it has been shown to reduce battery life by as much as 25%!), and results in dropped frames. The latter point is illustrated below, where the red dots are frames that get drawn on something like a canvas but never make it to the screen because another frame is drawn before the screen refreshes:

images

This is why it’s common to animate on multiples of 16.7ms using setInterval. However, using 16.7 assumes a 60Hz display refresh, which isn’t always the case. The right solution, then, for both Windows Store apps in JavaScript and web apps is to use requestAnimationFrame. This API simply takes a function to call for each frame:

requestAnimationFrame(renderLoop);

You’ll notice that there’s not an interval parameter; the function rather gives you a way to align your frame updates with display refreshes so that you draw only when the system is ready to display something:

images

What’s more, requestAnimationFrame also takes page visibility into account, meaning that if you’re not visible (and animations are thus wasteful), you won’t be asked to render the frame at all. This means you don’t need to handle page visibility events yourself to turn animations on and off: you can just rely on the behavior of requestAnimationFrame directly.

Tip If you really want an optimized display, consider doing all your app’s drawing work (not just animations) within a requestAnimationFrame callback. That is, when processing a change, as in response to an input event, update your data and call requestAnimationFrame with your rendering function rather than doing the rendering immediately. And always be mindful to redraw only when you need to redraw, as we’ll see in a moment, to make the best use of CPU and battery power.

It’s also good to know that attempting to animate a canvas that’s partly obscured by an element with display:inline-block has been found to result in very poor performance and large gaps between frames because of excessive region invalidation. Using a different display model such astable-cell avoids this issue.

Calling this method once will invoke your callback for a single frame. To keep up a continuous animation, your handler should call requestAnimationFrame again.

Tip Be mindful about JavaScript closures within the callbacks for requestAnimationFrame, setInterval, and setTimeout, especially when you renew the callback again. An accumulation of non-garbage-collected closures can accumulate large memory leaks, especially at 60fps!

Keeping up a continuous animation is shown in the Using requestAnimationFrame for power efficient animations sample (this wins fourth place for long sample names!), which draws and animates a clock with a second hand:

images

The first call to requestAnimationFrame happens in the page’s ready method, and then the callback refreshes the request (js/scenario1.js):

window.requestAnimationFrame(renderLoopRAF);
 
function renderLoopRAF() {
   drawClock();
   window.requestAnimationFrame(renderLoopRAF);
}

where the drawClock function gets the current time and calculates the angle at which to draw the clock hands:

function drawClock() {
   // ...
 
   // Note: this is modified from the sample to create a Date only once, not each time
   var date = new Date();
   var hour = date.getHours();
   var minute = date.getMinutes();
   var second = date.getSeconds();
 
   // ...
 
   var sDegree = second / 60 * 360 - 180;
   var mDegree = minute / 60 * 360 - 180;
   var hDegree = ((hour + (minute / 60)) / 12) * 360 - 180;
 
   // Code to use the context's translate, rotate, and drawImage methods
   // to render each clock hand
}

Here’s a challenge for you: What’s wrong with this code? Run the sample and look at the second hand. Then think about how requestAnimationFrame aligns to screen refresh cycles with an interval like 16.7ms. What’s wrong with this picture?

What’s wrong is that even though the second hand is moving visibly only once per second, the drawClock code is actually executing nearly 50, 60, or 100 times more frequently than that! Thus the “Efficient and Smooth Animations” title that the sample shows on screen is anything but! Indeed, if you run Task Manager, you can see that this simple “efficient” clock is ironically consuming a disproportionate amount of CPU. Yikes! (The percentage depends on your hardware, clearly—the 20% shown here is on an older laptop; my newer device shows more like 8%).

images

Remember that an interval aligned with ~16.7ms screen refreshes (on a 60Hz display) implies 60fps rendering. If you don’t need that much, you should skip frames yourself according to elapsed time, thereby saving power, and not blindly redraw as this sample is doing. In fact, if all we need is a once-per-second movement in a clock like this, repeated calls to requestAnimationFrame is sheer overkill. We could instead use setInterval(function () { requestAnimationFrame(drawClock) }, 1000) to coordinate 1s intervals with screen refreshes. If you make this change in theready method, for example, the CPU usage will drop precipitously (even down to less than 0.1%):

images

But let’s say we actually want to put 60fps animation and 20% of the CPU to good use. In that case, we should at least make the clock’s second hand move smoothly, which can be done by simply adding milliseconds into the angle calculation in the drawClock method (and reversing the previous setInterval change):

vardate =newDate();
var second = date.getSeconds() + date.getMilliseconds() / 1000;

Still, 8% to 20% is a lot of CPU power to spend on something so simple and 60fps is still serious overkill. ~10fps is probably sufficient for good effect. In this case we can calculate elapsed time within renderLoopRAF to call drawClock only when 0.1 seconds have passed:

varlastTime = 0;
 
function renderLoopRAF() {
   var fps = 10;// Target frames per second
   var interval = 1000 / fps;
   var curTime = Math.floor(Date.now() / interval);
 
   if (lastTime != curTime) {
       lastTime = curTime;
       drawClock();
   }
 
   requestAnimationFrame(renderLoopRAF);
}

That’s not quite as smooth—10fps creates the sense of a slight mechanical movement—but it certainly has much less impact on the CPU (about 1/4th of the 60fps usage):

images

I encourage you to play around with variations on this theme to see what kind of interval you can actually discern with your eyes. 10fps and 15fps give a sense of mechanical movement; at 20fps I don’t see much difference from 60fps at all, and the CPU usage is cut in half. You might also try something like 4fps (quarter-second intervals) to see the effect. In this chapter’s companion content I’ve included a variation of the original sample where you can select from various target rendering rates.

The other thing you can do in the modified sample is draw the hour and minute hands at fractional angles. In the original code, the minute hand will move suddenly when the second hand comes around to the top. Analog clocks don’t actually work this way: their complex gearing moves both the hour and the minute hand ever so slightly with every tick. To simulate that same behavior, we just need to include the seconds in the minutes calculation, and the resulting minutes in the hours, like so:

var second = date.getSeconds() + date.getMilliseconds() / 1000;
var minute = date.getMinutes() + second / 60;
var hour   = date.getHours() + minute / 60;  

In real practice, you’d generally want to avoid just running a continuous animation loop like this: if there’s nothing moving on the screen that needs animating (for which you might be using setInterval as a timer) and there are no input events to respond to, there’s no reason to callrequestAnimationFrame. Also, be sure when the app is paused that you stop calling request-AnimationFrame until the animation starts up again. (You can also use cancelAnimationFrame to stop one you’ve already requested.) The same is true for setTimeout and setInterval: don’t generate unnecessary calls to your callback functions unless you really need to do the animation. For this, use the visibilitychange event to know if your app is visible on screen. While requestAnimationFrame takes visibility into account (the sample’s CPU use will drop to 0% before it is suspended), you need to do this on your own with setTimeout and setInterval.

In the end, the whole point here is that really understanding the animation interval you need (that is, your frame rate) will help you make the best use of requestAnimationFrame, if that’s needed, or setInterval/setTimeout. They all have their valid uses to deliver the right user experience with the least consumption of system resources.

Did you know? One change introduced with Windows 8 and Internet Explorer 10 (and thus supported in subsequent versions) is that setTimeout and setInterval, along with setImmediate, all support including parameters that you can pass to the callback functions.

What We’ve Just Learned

• In PC Settings (or the desktop control panel), users can elect to disable most (that is, nonessential) animations. Apps should honor this, as does WinJS, by checking the Windows.UI.ViewManagement.-UISettings.animationsEnabled property.

• The WinJS animations library has many built-in animations that embody the Windows personality. These are highly recommended for apps to use for the scenarios they support, such as content and page transitions, selections, list manipulation, and others.

• All WinJS animations are built using CSS and thus benefit from hardware acceleration. When the right conditions are met, such animations run in the GPU and are thus not affected by activity on the UI thread.

• Apps can also use CSS animations and transitions directly, according to the W3C specifications.

• Apart from WinJS and CSS, apps can also use functions like setInterval and request-AnimationFrame to implement direct frame-by-frame animation. The requestAnimationFrame method aligns frames with the display refresh rate, leading to the best overall performance.

102 Note that the sample erroneously passes a variable output as the first parameter to exitContent and enterContent; the code should appear as shown here, with outgoing passed to exitContent and incoming passed to enterContent.

103 In this code, any existing style classes will be removed from the elements by virtue of setting className. If you want to add and remove classes without affecting others, use the WinJS.Utilities.addClass and removeClass methods.

104 For more on this topic, see http://dvcs.w3.org/hg/webperf/raw-file/tip/specs/setImmediate/Overview.html.

105 Or possibly multiple layered canvases, where you can isolate different animation groups on their own canvases. For more on this, see Optimize HTML5 canvas rendering with layering (IBM developerWorks).

106 It's also worth noting that you could use the MediaStreamSource that we discussed at the end of Chapter 13, "Media," to dynamically generate a video, which is effectively an animation but would then provide playback controls, Play To capabilities, and so forth.