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 8 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 (and is, in fact, a Store certification requirement). 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 perhaps overuse animations, simply using animations 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.)
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 8 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.
Before we go any further, there’s a setting buried deep inside the desktop Control Panel’s Ease of Access Center that you need to know about because it affects how the WinJS Animations Library behaves and should affect whether you do animations of your own. From the desktop, invoke the Settings Charms and select Control Panel. Then navigate to Ease of Access > Ease of Access Center > Make The Computer Easier To See. Scroll down close to the bottom and you’ll see the item “Turn off all unnecessary animations (when possible),” as shown in Figure 11-1.
FIGURE 11-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 Control Panel setting can be 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 obviously 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.enable-Animations
and WinJS.UI.disableAnimations
, the effects of which are cumulative, and the animationsEnabled
property counts as 0 if the Control Panel option is checked and 1 if it’s unchecked.
When implementing your own animations either with CSS or with mechanisms like setInterval
or requestAnimationFrame
, it’s a good idea to 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 it can be turned off without ill effect.
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 basically 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 8 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 the Animating Your UI and Animating UI Surfaces topics 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 like WinJS.UI.Appbar
. You’ll primarily use them when implementing UI directly with HTML layout or when building custom controls.
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 or animation metrics can be important when creating web-based content to integrate with your app. As noted before the Windows 8 app certification requirements, specifically section 4.5, indicates that touch targets must provide visual feedback. Because you cannot 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, you should acquire these animation metrics programmatically through the API in Windows.Core.UI.AnimationMetrics
, rather than hardcoding any values. 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. 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.
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 same. 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 your completed handler 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.
Do 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 MSPointerDown
and MSPointerUp
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 function. 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 give 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 that 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:55
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 create the animation, manipulate the DOM, and then 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.
Developers have often asked 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 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 event 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.
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.executeTransition
helpers) 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.
We’ll come back to this subject in a bit when we look at sample code. As I assume that you’re already at least a little familiar with the subject, let’s first review how CSS animations and transitions work. I say animations and transitions both because there are, in fact, two separate CSS specifications: CSS animations and 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 as 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 than display: 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. 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):
@keyframes move { from { transform: translateX(0px); } 50% { transform: translateX(400px); } to { transform: translateX(800px); } }
More generally:
• Start with @keyframes <identifier>
where <identifier>
is whatever name you want to assign to the keyframe (like move above). You’ll refer to this identifier elsewhere in style rules.
• Within this keyframe, you create any number of 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 rule set you then 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 the animation-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 keyframe. 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 keyframe to apply. The other animation-*
styles then describe how the keyframe should execute:
• animation-duration
(animationDuration
in JavaScript) The duration of the animation in seconds (fractions allowed, of course). 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 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.
• 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
.
• animation-play-state
(animationPlayState
in JavaScript) Allows you to play or pause an animation. The default state of running
plays the animation; setting this to paused
will pause it until you set the style back to running
.
• 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 play-state) in the order of name, duration, timing function, delay, iteration count, direction, and fill mode.
Applying a style that contains animation-name
will trigger the animation for that element. This can happen automatically if the animation is named in a style that’s applied by default. It can also happen on demand if the style is assigned to an element in JavaScript or if you set the animation
property for an element.
Keyframes, while 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):
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 this code uses setImmediate
to yield to the UI thread before setting the animationName
property that will trigger the animation. This makes sure that other code that 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.”56 Such a pattern is typically for triggering one or more animations once everything else is set up.
As a final note for this section, you might be interested in The Guide to CSS Animation: Principles and Examples from Smashing Magazine. This will tell you a lot about animation design beyond just how CSS animations are set up in your code.
Turning now to 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 green ball on the bottom, on the other hand, continues to move smoothly the whole time.
What’s tricky to understand about this sample is that both balls use the same CSS style rule named ball that we saw in the previous section. 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, as 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 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 lets 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 11-2. 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.
FIGURE 11-2 Output of Scenario 3 of the HTML independent animations sample.
Scenarios 4 and 7 then show something we haven’t talked about yet, 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 are 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.
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 an timer with setInterval
, do some calculations in the handler and update elements (or draw on a canvas
), 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.)
As such, there is considerable wisdom available in the community on this subject 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. 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 (even LCD and LED 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:
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 Windows Store apps in JavaScript and web apps both, 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:
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 drawing work of your app (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 as table-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. This is shown in the Using requestAnimationFrame for power efficient animations sample (this wins second place for long sample names!), which draws and animates a clock with a second hand:
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 only create a Date 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 15–20% of the CPU. Yikes!
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 the ready
method, for example, the CPU usage will drop precipitously:
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:
var second = date.getSeconds() + date.getMilliseconds() / 1000;
Still, 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:
var lastTime = 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:
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 is running at about 7–10%. 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 hand at fractional angles. In the original code, the minute hand will move suddenly when the second hand comes around to the top. Many 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, like a game, 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 call requestAnimationFrame
. Also, be sure when the app is paused that you stop calling requestAnimation-Frame
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 right level of consumption of system resources.
Did you know? One change for Windows 8 and Internet Explorer 10 is that setTimeout
and setInterval
, along with setImmediate
, all support including parameters you can pass to the callback functions?
• In 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 8 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 requestAnimation-Frame
to implement direct frame-by-frame animation. The requestAnimationFrame
method aligns frames with the display refresh rate, leading to the best overall performance.