On the desktop, most of the “animations” you have likely seen aren’t actually CSS animations, but rather Flash, Canvas, or JavaScript animations. On mobile devices, it is important to use CSS3 transitions, transforms, and animations to animate elements whenever possible instead of these other techniques.
So why not animate with Flash, JavaScript, or <canvas>
? Flash is not supported on mobile
iOS devices, and never will be. Adobe, the makers of Flash, discontinued
development of the mobile Flash Player, with the last release being Flash
Player 11.1 for Android and the BlackBerry PlayBook in late 2011.
Flash is still being developed for desktop, but with Flash not supported or installed on any new devices, you’ll be missing out on a huge chunk, if not all, of the mobile market. And, for those using older devices with browsers that support Flash, you’ll be draining their battery.
Similarly, animations done with JavaScript, when not hardware-accelerated, block the UI thread. This can make the rest of the application, if not the animation itself, choppy, nonresponsive, a memory hog, and a battery drainer.
On mobile, CSS animations are an awesome alternative to these other technologies. Browsers are optimized to handle CSS, so you lose less memory, CPU, and battery.[67] And, animations, transforms, and transitions are supported on all modern smartphone browsers, so there is no reason to not use them.
Well, there is. An important note about transitions, transforms, and animations: just because you can, doesn’t mean you should. Use instructional animations to show procedures or tasks that are hard to describe with static pictures. Accompany the instructions with text that explains the animation.
Animation can be used to draw attention to an element on the page, such as a form submission error message, a success message on a one page application, or an update to the page that is important that the user may not otherwise have noticed.
Don’t animate unless you want to draw attention, and don’t animate elements that need to be read or interacted with. Games, of course, are an exception to these rules.
These features of CSS are very captivating when used wisely and sparingly. Don’t overuse, unless you’re using these features in gaming, or your site will be reminiscent of a Geocities site from the 1990s.
Transitions allow CSS properties to change from one value to another over a
period of time. If you’ve ever used :hover
to change the color of a link, you’ve
used a transition, but likely a transition of zero milliseconds in
duration. With CSS transitions, you can make that color change, and a
whole lot of other properties change, over a period of time.
CSS transitions apply to any change from one state to another state.
The transition
shorthand
property is made up of four properties: (1) transition-property
, which defines which properties are affected, (2) transition-duration
, which sets the during of the transition effect, (3) the transition-timing-function
, which delineates how the timing will accelerate, decelerate, or
otherwise change during the transition, and (4) the transition-delay
, which sets how long to wait before starting the transition after
the transition is initiated.
To create a transition, set the styles of the element you want to transition. In the properties of the initial state, you include the name of the property or properties you want to affect in the transition, along with the time of the transition, the speed, and the delay, if any. Here is the syntax as shown in the spec:[68]
nav a { background-color: rgb(255,255,255); border: 5px solid #000000; transition-property: background-color; transition-timing-function: linear; transition-duration: 0.8s; transition-delay: 200ms; }
This is the initial keyframe. A keyframe is a drawing that defines the starting or ending points of any smooth transition in animation (or in film). Keyframes aren’t explicitly employed in transitions. They are used when creating animations using the animation properties, which we discuss later in this chapter. Getting an understanding now may help make animations less confusing later.
You then define the value of the properties listed in the value of
transition-property
: in this case, the
value of the background-color
. We could
have used the key term all
if we had
wanted to transition all the transitionable properties that change between
the default and transitioned state, such as the hover state:
nav a:hover, nav a.hover { background-color: rgb(0, 0, 0); border: 5px dashed #CCCCCC; }
In the preceding example, when the user hovers over a link in the navigation section of the document, the background of the link will go from white to black, as shown in Figure 10-1. The border color and style will change immediately on hover. The transition of the background color will start after 200 milliseconds; take 800 milliseconds to transition, and transition at an even keel. In Figure 10-1, you’ll note the border changed immediately, but the background color waited the 200 ms delay before starting to transition.
This may be a lot to grasp, so let’s go over the various transition values. There are no screenshots in this section, since “effects” are hard to perceive in print. However, there are examples in the online chapter resources.
The transition-property
lists
the properties that will be transitioning during the animation.
Properties that can be made to transition include:
background-color
background-position
background-size
border
(color and width,
but not style)
border-color
border-radius
border-width
border-spacing
box-shadow
bottom
clip
color
columns
column-width
column-count
column-gap
column-rule
(color and
width, but not style)
crop
flex
flex-basis
flex-grow
(except from or
to 0)
flex-shrink
(except from or
to 0)
font-size
font-size-adjust
font-stretch
font-weight
height
left
letter-spacing
line-height
margin
max-height
max-width
min-height
min-width
opacity
order
(part of flex)
outline-color
outline-offset
outline-width
padding
perspective
perspective-origin
right
text-decoration-color
text-indent
text-shadow
top
transform
transform-origin
vertical-align
visibility
width
word-spacing
z-index
In general, any pair of property values where you can conceivably figure out a midpoint is transitionable. Note that depending on the transition timing function, the midpoint between two values may not display at the middle of the transition. Ease in, ease out, and other cubic-bezier timing function transitions will have different midpoints.
For example, top: 0
to top: 100px;
has a midpoint of 50 px and is
therefore transitionable, but display:
block
to display: none;
(used as example property values in Table 10-1)
does not have a midpoint and is not transitionable. You can transition
from height: 600px
to height: 700px;
but not from height: auto;
to height: 700px;
.
Property | Initial value | Final value | Midpoint? | Transitionable |
height | 100 px | 200 px | 150 px | ✓ |
height | auto | 200 px | ? | ✗ |
opacity | 0 | 1 | 0.5 | ✓ |
display | block | none | ? | ✗ |
While you can’t currently transition background images, including
gradients, you can transition background-position
and background-size
to create some interesting
effects.
The exception to this midpoint rule is visibility
. Visibility is a property that seemingly you wouldn’t be able to
transition, but you can include it in transitions and animations. The
value will actually jump from visible
to hidden
at the end of the
transition effect if those are the property values set. There is
discussion of making all properties transitionable and animatable, but
we aren’t there yet.
The value of the transition-property
property is a
comma-separated list of any number of these property names, or the
keyword all
. Only the properties
listed as the value of the transition-property
will transition over time
when the transition gets initiated, unless all
is declared:
nav a { -webkit-transition-property: background-color; /* iOS6-, BB, Android, Ch25-*/ -moz-transition-property: background-color; /* FF4 to 15 */ -o-transition-property: background-color; /* O 10.5 to 12 */ transition-property: background-color; /* IE10, FF16+, 012.5+, Ch26+, iOS7 */
I’ve included -moz-
and
-o-
, to demonstrate support for older
browsers. -ms-
has never been needed,
and Firefox and Opera are not needed in currently used mobile browsers.
The only prefix we need to include for the transition properties in the
mobile space, or at all, is -webkit-
.
If a property that is not transitionable is included in the value
of the transition-property
property,
the value will be ignored, changing state instantly
instead of transitioning over time. The value that can’t transition over
time will not, but the transition itself will not fail.
The transition-duration
property sets how long the transition will take to go from start to
finish, from the first keyframe to the last keyframe. You can set how
many seconds or milliseconds the transition will take to animate from
the original value to the transitioned value.
We learned about time units in Chapter 8. This is the first time we use it. The following 2 lines are all equal, using millisecond or second units (but target different browsers):
-webkit-transition-duration: 0.5s; transition-duration: 500ms; ...
The transition-timing-function
enables control over the transition, describing how the animation
will proceed over time. The value can take one of several
keywords—ease
, linear
, ease-in,
ease-out
, ease-in-out
,
step-start
, step-end
, steps(x,
start)
, steps(x, end)
—or
take as its value a cubic Bézier function.
A cubic Bézier takes as its value four points in a plane: starting at the first point going toward the second, and arriving at the last point from the direction of the third point. After about three years of calculus, it might make sense! Fortunately, some cubic Bézier curve values are included in CSS3 as predefined keywords, and links to tools to help you better understand cubic Bézier are included in the online chapter resources.
The nonstep
keyword values
(ease
, linear
, ease-in-out
, etc.) are each themselves
representing cubic Bézier curve with four fixed-point values:
ease
, or cubic-bezier(0.25, 0.1, 0.25, 1.0)
The default value; increases in velocity toward the middle of the transition, slowing back down at the end.
linear
or cubic-bezier(0.0, 0.0, 1.0, 1.0)
Transitions at an even speed.
ease-in
or cubic-bezier(0.42, 0, 1.0, 1.0)
Starts off slowly, with the transition speed increasing until complete.
ease-out
or cubic-bezier(0, 0, 0.58, 1.0)
Starts transitioning quickly, slowing down as the transition continues.
ease-in-out
or cubic-bezier(0.42, 0, 0.58, 1.0)
Starts transitioning slowly, speeds up, and then slows down again.
cubic-bezier(p1,p2,p3,p4)
Where the p1
and p3
values must be in the range of 0 to
1.[69]
The step functions—steps(x,
end)
, steps(x, start)
,
step-end
, and step-start
—divide the duration of the
transition into equal lengths of time. Each interval is an equal step in
terms of time taking the transition from original state to final state.
The function also specifies whether the step is at the start or end of
the interval.
In other words, if there are five steps, steps(5, start)
will have steps representing
0%, 20%, 40%, 60%, and 80% of the progress toward the final state. If
you set steps(5, end)
, you will have
steps representing 20%, 40%, 60%, 80%, and 100% of the progress.
The value step-start
is
equivalent to steps(1, start)
and
step-end
is equivalent to steps(1, end)
. The steps()
values are good for animating
background image sprites to create animations. There are some examples
of these values in the online chapter
resources. Continuing on with our sample code:
... -webkit-transition-timing-function: linear; transition-timing-function: linear; ...
The transition-delay
property
specifies the number of milliseconds or seconds to wait
between a change of state causing the transition and the start of the
transition effect. The default value of 0s
indicates that the animation should begin
to transition immediately. Positive time values will delay the start of
the transition effect for the value indicated. Negative values cause the
transition to start right away, but start midway through the
transition.
While transition-delay
may seem
like a useless property, it can greatly help improve user experience.
Oftentimes you don’t want hover or touch effects to be too sensitive. If
a user is dragging a finger or mouse quickly across the screen to get
from point A to point B, you don’t want all the points in between that
are accidentally touched to react to the touch too quickly. A transition
delay of 50 ms generally does the trick. The intentional touches still
seem reactive, but the page doesn’t flicker as object transitions get
unintentionally activated.
Negative transition delays can also serve a purpose. As long as
the absolute value of the delay is less than the transition-duration
, the transition, when
initiated, will start midway through the transition at a point
proportional to the time difference. For example, if you have a 10 second transition-duration
, and a −4 second delay,
the transition will start immediately, but at 40% through the
transition:
... -webkit-transition-delay: 250ms transition-delay: 0.25s; }
Putting it all together will seem a little crazy:
nav a { -webkit-transition-property: background-color; transition-property: background-color; -webkit-transition-duration: 0.5s; transition-duration: 500ms; -webkit-transition-timing-function: linear; transition-timing-function: linear; -webkit-transition-delay: 250ms; transition-delay: 0.25s; }
Instead of including 8, 12, or 16 lines[70] of code for a transition, the transition
shorthand property, which combines
the four properties just covered, can be used. Note that the order of
the duration and delay values is important (i.e., they need to be in the
same order as introduced earlier):
transition-property
transition-duration
transition-timing-function
transition-delay
nav a { background-color: #FFFFFF; -webkit-transition: background-color 500ms linear 250ms; transition: background-color 500ms linear 250ms; } nav a:hover, nav a.hover { background-color: #FF0000; }
By putting the transition on the default state, when the element is hovered or changes class, the transitioned properties will transition to their new values, reversing the transition on mouse change/touch end or if the new class is removed. The transition properties can be put on the default state or on a different state. It depends when and how you want the element to transition.
But what if you want to transition more than one property? Maybe you want to
change background-color
, border-color
, and color
all on the same property? The transition
properties allow for multiple transitions in one call.
Let’s say instead of just transitioning the background-color
property, we want to
transition the border
property as
well (you can transition the border color and width but not style). We
would have to (1) include the new border
property in the transitioned style
declaration, and (2) include the border
property in the transition-property
value list, either as a
comma-separated series of values, or use the key term all
. Note that all
should only be used if you want to
transition all properties in the same way.
For instance, in earlier examples, although we defined the
border
and background-color
properties in both the touch
or hover states, we only included the background-color
property as the value of the
transition-property
property. In
those examples, the background-color
will transition slowly (over 250 ms), and the border color will change
immediately on hover or touch: like you are used to, like it has always
done without transition, as if the transition were set to a 0 s duration
with a 0 s delay.
If you want to change both properties at the same rate and delay,
you could write your transition
shorthand using the all
keyword,
since you are transitioning all the properties listed in the hover
status:
nav a { background-color: #FFFFFF; border: 5px solid #CCCCCC; -webkit-transition: all 500ms linear 250ms; transition: all 500ms linear 250ms; }
When using the all
keyword, all
the properties transition at the same rate, speed, and delay. If you
want some but not all of your properties to transition at the same rate,
timing, and delay, comma separate the transition-property
properties:
nav a { background-color: #FFFFFF; border: 5px solid #CCCCCC; color: red; -webkit-transition: border, color 500ms linear 250ms; transition: border, color 500ms linear 250ms; }
If you don’t want all your properties to transition at the same
rate, want some to have a greater delay than others, or if you just want
a few properties to have a transition effect, include the various
transition properties as a comma-separated list, including, at minimum,
the transition-property
and transition-duration
for each:
nav a { background-color: #FFFFFF; border: 5px solid #CCCCCC; color: red; -webkit-transition: background-color, color 500ms linear 750ms, border 500ms linear 250ms; transition: background-color, color 500ms linear 750ms, border 500ms linear 250ms; }
In this example, the border, which has the shortest transition-delay
, will transition first. When
the border has finished transitioning, at the 750 ms point—which is the
transition-delay
property, and the
value of the 500 ms for the border transition plus the 250 ms delay—the
background-color
and color
will both then transition over half a
second:
transition-property: background-color, color, border; transition-duration: 500ms; transition-timing-function: linear; transition-delay: 750ms, 750ms, 250ms;
We could also have used the longhand properties, with each property comma separated, but that would have been more code in this scenario, as shown previously. I find the shorthand syntax easier to write, generally shorter to write, easier to understand, and easier to maintain.
In CubeeDoo, we transition our card flips over 0.25 s. We haven’t learned how to flip a card yet (it’s covered in the next section), but we can tell it to flip everything, immediately over 0.25 s with:
1 #board > div { 2 position: relative; 3 width: 23%; 4 height: 23%; 5 margin: 1%; 6 float: left; 7 -webkit-transition: 0.25s; 8 transition: 0.25s; ...
In the default, pre-flipped state, in line 7 and 8, we tell the
cards that when the state is changed, transition all (default for
transition-property
) the properties
immediately (default for transition-delay
) in the default transition-timing-function
of ease
.
I wrote transition: 0.25s
, but
I could have also written transition: all 0.25s
ease 0ms;
, which may be longer, but might be easier to
maintain as author intentions are clearer with the latter.
Transforms allow you to resize, rotate, skew, translate, and otherwise reposition elements. There is a 2D version of the transforms module that is supported by all browsers, starting in IE9,[71] and another for 3D, with support spreading quickly.
Unlike transitions, transforms are supported in IE9.
CSS3 transforms allow for various transformations to be applied to an element, including multiple transforms on a single element.
Two CSS properties are used to create a transform: the transform
property specifies the types of transformations you want to apply to
the element, and the transform-origin
property sets the point of origin from where the transform takes
place.
The first step we’re covering is setting the origin of the transform. The transform-origin
property establishes the
origin of transformation for an element.
The default transform-origin
value is 50% 50% 0
, which is the
center of the element. The first value specified is the
x coordinate, or left/right value, and the second
value is the y coordinate, or the top/bottom value,
with the values being calculated from the top left corner of the
element. The third value is the z-offset, which is relevant when doing
3D transforms. The values can be specified using a length, a percentage,
or the keywords left
, center
, right
, top
,
center
, and bottom
, with the optional z-offset being a
length only that is not a percentage.
The point set by the transform-origin
is the point around which the
transform will occur. As Figure 10-2 demonstrates, when
the point of origin is set to the center point of an element (the
default), the element will transform, in this case rotate, around that
center point. When the transform-origin
is set to a different
location, such as the top left
as
shown in Figure 10-2, the
element transforming, such as rotating, around the origin point in the
top left will create a different effect. The element orbits around this
point.
The transform-origin
on the
left of Figure 10-2 is the
default, so can be omitted. The syntax for the effect on the right can
be written as:
-webkit-transform-origin: top left 0; /* all webkit & blink browsers */ -moz-transform-origin: top left; /* FF 3.5 - 15 */ -ms-transform-origin: 0 0; /* IE9 */ -o-transform-origin: 0 0 0; /* O 11.0-12.0 */ transform-origin: top 0 0; /* IE10+, FF16+, O12.1 only */
... where top left
, 0 0
, 0 0 0
,
and top left 0
are all
equivalent.[72] Once the point of origin is set (or omitted, so set to the
default value of transform-origin: center center 0;
),
the type of transform is applied. This is set with the transform
property with a list of one or more
transforms as the value.
Supported in Firefox 3.5+, Opera 10.5, Internet Explorer 9, and WebKit since before
the iPhone even came out, the CSS transform
property lets you modify the
coordinate space of the CSS visual formatting model. Using it, elements
can be translated, rotated, scaled, and skewed. CSS transforms modify
the coordinate space, allowing us to change the position of the affected
content without disrupting the normal flow. The location and amount of
space a transformed element takes is the location and space used by the
element before transforms were applied.
We manipulate an element’s appearance using transform
functions. The value of the transform
property is a list of
space-separated transform functions applied in the order provided. The
transform
functions include:
The translate(x, y)
function,
as shown in Figure 10-3, is similar to
relative positioning, translating, or relocating, an element by x from
the left, and y from the top:
-webkit-transform: translate(15px, −15px); -ms-transform: translate(15px, −15px); transform: translate(15px, −15px);
The translateX(x)
function is
similar to the translate()
function, but only the
left/right value is specified:
-webkit-transform: translatex(15px); -ms-transform: translatex(15px); transform: translatex(15px);
The translateY(y)
function is
similar to the translate()
function, but only the
top/bottom value is specified:
-webkit-transform: translatey(-15px); -ms-transform: translatey(-15px); transform: translatey(-15px);
The scale(w, h)
property,
as shown in Figure 10-4, scales an
element by w
width and h
height:
-webkit-transform: scale(1.5, 2); -ms-transform: scale(1.5, 2); transform: scale(1.5, 2);
If only one value is declared, the scaling will be proportional. Since you likely don’t want to distort an element, you’ll generally see only one parameter in this transform function:
transform: scale(2);
Note that when you use transform to scale up, your element may
appear blurry, as would be expected when you scale up images. For this
reason, I generally recommend that if you need to scale up, start with
an element that has been scaled down, and then scale up to scale(1)
.
The scaleX(w)
function
is similar to the scale()
function, but only the width value
is specified. It is the same as declaring scale(w, 1)
:
-webkit-transform: scalex(0.5); -ms-transform: scalex(0.5); transform: scalex(0.5);
The -o-
and -moz-
prefixing for transforms is
excluded, as Mozilla and Presto support transforms without a vendor
prefix.
The scaleY(h)
function
is similar to the scale()
function, but only the height value
is specified. It is the same as declaring scale(1, h)
:
-webkit-transform: scaley(2); -ms-transform: scaley(2); transform: scaley(2);
The rotate(angle)
function,
as shown in Figure 10-5, will rotate an
element about the point of origin (featured in Figure 10-2) by the angle
value specified:
-webkit-transform: rotate(15deg); -ms-transform: rotate(15deg); transform: rotate(15deg);
The rotateX(angle)
function
will rotate an element about its x-axis, offset by the
origin point if one is set:
-webkit-transform: rotatex(15deg); -ms-transform: rotatex(15deg); transform: rotatex(15deg);
Rotating an element 90 deg along the x-axis will make it
disappear, and 180 deg will flip it completely over so that you see it
upside down from the backside. Whether or not you see the contents
when flipped can be set with the backface-visibility
property described .
Similar to rotateX()
,
the rotateY(angle)
function will rotate an element about its y-axis at the angle value
specified:
-webkit-transform: rotatey(15deg); -ms-transform: rotatey(15deg); transform: rotatey(15deg);
In CubeeDoo, we use rotateY(180deg)
in our animation when the
user clicks on a card, flipping the card container, so the back of the
card is hidden and we can see the face. We then animate it back with
rotate(0deg)
, or if you prefer
rotate(360deg)
for a continuous
rotation, if the player was not successful in making a match.
The skew(x,y)
function,
as shown in Figure 10-6, specifies a
skew along the x- and y-axis. The x
specifies the skew on the x-axis, the y
specifies the skew on the y-axis. If there
is only one parameter, then it’s the same as skew(x, 0deg)
, or skewX(x)
. The values are angles, degrees,
turns or grads:
-webkit-transform: skew(15deg, 4deg); -ms-transform: skew(15deg, 4deg); transform: skew(15deg, 4deg);
The skewX(x)
function is
similar to the skew()
value, but only the x-axis value is specified, and the skew will only
be along the x-axis, instead of both the x- and y-axis. The top and
bottom of the box will stay level, and the left and right will
skew:
-webkit-transform: skewx(15deg); -ms-transform: skewx(15deg); transform: skewx(15deg);
The skewY(y)
function is
similar to the skew()
value, but only the y-axis value is specified. It is similar to
declaring skew(0deg, y)
. The left
and right sides of the box will stay vertical, and the top and bottom
will skew:
-webkit-transform: skewy(-3deg); -ms-transform: skewy(-3deg); transform: skewy(-3deg);
Declaring skewX(x)
or
skewY(y)
is similar but not the
same as declaring skew(x, 0deg)
and
skew(0deg, y)
, respectively. If you
only declare one of the two on an element, it is indeed the same
thing, but declaring skew(x, 0deg)
and skew(0deg, y)
would lead to the
latter overwriting the former, whereas skewX(x)
or skewY(y)
would actually be equivalent to
writing skew(x, y)
; because the
properties are being combined, rather than overwritten. Figure 10-6 shows the skew
functions.
The previous section showed single transforms, but you can include more than one transform on an element. To include more than one transform, simply separate the transform functions with spaces:
.enlargen:hover, .enlargen.hover { -webkit-transform: translate(−50%, −50%) scale(2) rotate(0deg); -ms-transform: translate(−50%, −50%) scale(2) rotate(0deg); transform: translate(−50%, −50%) scale(2) rotate(0deg); }
This makes the element twice as tall and twice as wide. By
translating the element 50% up and to the left, the bottom-right corner
should remain in the exact same location. Declaring rotate(0deg)
is unnecessary, since any
transforms declared with a selector with weaker specificity that
included a rotate
function would be overwritten,
regardless of whether we included the rotate
function. I’ve included this as a reference for how to include the
rotate()
transform function, and to remind you to
include the unit whenever you are using non-length units.
Even though the rotation value is zero degrees, you must include the unit for degrees, just as you must with time (s or ms), rads, grads, turns, Hz, and kHzs.
Note that the transition-property
property values are comma
separated, and the transform
functions are space separated.
This enlargen
class may be
something you would want to add to an image gallery, highlighting an
image that is hovered by making it four times larger (twice as wide and
twice as tall) and remove any tilt that might have been interesting as a
thumbnail, but tacky in full size.
The matrix()
transform
function is a single function that defines the
translation, skew, rotation, and scaling of an element. It takes six
parameters. If you use a tool to create a transform, the software
generally produces a matrix
function rather than four separate transform functions. The two
following lines could be equal, with the last two matrix values
depending on the size and location of the element being
transformed:
transform: translate(−50%, −50%) scale(2) rotate(0deg); transform: matrix(2, 0, 0, 2, −100, −172.5)
Generally, if you see a matrix
in CSS markup, it has been computer
generated. You likely won’t ever write a matrix
value. I included it here so you
understand what it is, but not necessarily exactly what it means, if
you come across it.
Under the transition-property
property, I listed all the properties that could be transformed. If you
include the all
keyword when
declaring the transition-property
, or
the shorthand transition
, the
transform
property will be included
as part of all
.
You can declare the transform individually as part of the comma-separated list of transition properties. If including the actual transform as a property in the list, it should include the browser vendor prefix if the browser requires it:
p { -webkit-transition: -webkit-transform 500ms linear 250ms; transition: transform 500ms linear 250ms; -webkit-transform: translate(0) rotate(0deg); -ms-transform: translate(0) rotate(0deg); transform: translate(0) rotate(0deg); } p:hover { -webkit-transform: translate(100px, −100px) rotate(90deg); -ms-transform: translate(100px, −100px) rotate(90deg); transform: translate(100px, −100px) rotate(90deg); padding: 3px; border: 5px solid #00ff00; }
We included -ms-
for the
transform but not the transition, since transitions only received
support in IE10, and transforms are prefixed in IE9 but not IE10.
Browsers have been a bit slower in supporting 3D transforms, but they’re getting there. 3D transforms have been supported since iOS 3.2, Android 3, Blackberry 10, Firefox 10, IE10, Safari 4, and Chrome 12, all with vendor prefixes. 3D transforms are slated to be supported in Opera 15 with the Blink rendering engine. 3D transforms have only been supported since iPhone 2 (not the original), and is only supported if you have Mac OS X v10.6 or newer.
CSS 3D transforms enable positioning elements on the page in three-dimensional space. Just like before, you can combine 3D transforms with transitions (and animations described later) to create 3D motion.
Similar to the 2D transform functions, most browsers support 3D
transform properties, starting with IE10. As of this writing, the
-webkit-
prefix is required for
WebKit and Blink browsers.
A few things to note about 3D transforms is that elements that are transformed into a 3D space (1) are hardware-accelerated, and (2) have their own stacking context.
The translate3d(x, y, z)
function moves the element x
to the right, y
from the top, and
z
toward the user (away if the
value is negative). Unlike x
and
y
, the z
value cannot be a percentage.
The translateZ(z)
function
is similar to the translate3d()
function, but only affects the
z
positioning. A positive z
moves the element toward the user, and a
negative value moves the element away from the user. The parameter can
be any length units other than percentages.
Because of the benefits of hardware acceleration, translateZ(0)
is often used as a cure-all
(similar to how zoom:1
was a
panacea for IE6), to put rendering onto the GPU and out of the CPU, improving rendering
performance.
When it comes to paint times, the GPU performs better than the CPU. In animating elements with not insignificant paint times, getting the element elevated onto its own layer on the GPU, called a RenderLayer, will be faster and make your animation less janky. When it sits on its own layer, any 2D transform, 3D transform, or opacity changes can happen purely on the GPU, which will stay extremely fast—providing for the capability of frame rates over 60 fps.
Note, as elements in a 3D context have their own stacking
context, any elements you want to appear above the transformed
elements (as though they have a higher z-index), which are not nested
within the transformed element, must also be transformed into the 3D
space. To do this, developers attach transform: translateZ(0)
to elements that
would otherwise not need to be transformed.
The scale3d(w, h, z)
function
scales the element’s width (w
), height (h
), and z-scale (z
). The z-scale affects the scaling along
the z-axis in transformed children.
Similar to the scale3d()
function, the scaleZ(z)
provides a value for the z-scale only, affecting the scaling along the
z-axis in the element and its descendants that are not absolutely
positioned.
The rotate3d(x, y, z, angle)
function will rotate an element in 3D space. The first two forms
simply rotate the element about the horizontal and vertical axes.
Angle units can be degrees (deg), radians (rad), or gradians (grad).
The last form allows you to rotate the element around an arbitrary
vector in 3D space; x
, y
, and z
should specify the unit vector you wish to rotate around. The browser
will normalize the appearance.
The perspective(p)
transform
function allows you to put some perspective into the
transformation matrix. The perspective()
transform function allows you
to get a perspective effect for a single element:
transform: perspective(100px) rotatey(3deg);
Perspective can also be applied with the perspective
property, which applies to the
children of an element.
Earlier we were introduced to some transform properties. To successfully implement 3D transforms, we are provided with some new properties, and enhancements to some properties introduced for 2D transforms.
As we learned earlier, the transform-origin
property establishes the origin of transformation for an element. In
3D transforms, this property now accepts a z-offset value. transform-origin
accepts three values,
allowing you to specify a z-offset for the transform origin:
transform-origin: 0 0 500px;
While the effect only got support starting with Safari 4+ on Mac
OS X v10.6 and newer, and iPhone 2.0 and newer (not the original
iPhone), earlier browser versions that supported transform-origin
support this property as if
only two values are declared.
Not to be confused with the 3D transforms perspective()
function, the perspective
property, written with browser
prefix, is used to give an illusion of depth; it determines how things
change size based on their z-offset from the z=0 plane.
Objects on the z=0 plane appear in their normal size. Something at a z-offset of p/2 (halfway between the viewer and the z=0 plane) will look twice as big, and something at a z-offset of –p will look half as big. Thus, large values give a little foreshortening effect, and small values lots of foreshortening. Values between 500 px and 1,000 px give a reasonable-looking result for most content.
The default origin for the perspective effect is the center of
the element’s border box, but you can control this with perspective-origin
.
Perspective does not affect the element directly, but rather it affects the appearance of the 3D transforms on the transformed descendants of that element, enabling the descendants to share the same perspective as they are translated, or moved, around the viewport.
So, you’re wondering, what’s the difference between perspective: 600px
and transform: perspective(600px)
? You would
attach the former to a parent element so that all of the descendants
have the same vanishing point. You would attach the latter to the
actual element you’re transforming to give it its own
perspective.
The transform-style
property
defines how nested elements are rendered in 3D space.
All of these 3D transform effects are just painting effects. Those
transformed children are still rendering into the plane of their
parent; in other words, they are flattened.
When you start to build hierarchies of objects with 3D
transforms, parents and children should live in a shared
three-dimensional space and share the same perspective, which
propagates up from some container, not flattened. This is where
transform-style
comes in.
The transform-style
property
takes one of two values: flat
and
preserves-3d
. The default value of
flat
flattens the transformed
children into the plane of their parent. The preserves-3d
value dictates that those
children live in a shared 3D space with the element.
The backface-visibility
property specifies whether the element is visible or not when that element
is transformed such that its back face is toward the viewer. The
property takes one of two values: visible
(the default) or hidden
.
For example, in CubeeDoo, we flip a two-sided card to show the
card face when the card is selected. When we see the face, we don’t
want to see the back of the card, and vice versa, so we could set backface-visiblity
to hidden
.
We use a lot of the features just listed in our card flipping in CubeeDoo:
#board > div { position: relative; width: 23%; height: 23%; margin: 1%; float: left; -webkit-transition: 0.25s; transition: 0.25s; -webkit-transform: rotatey(0deg); transform: rotatey(0deg); -webkit-transform-style: preserve-3d; transform-style: preserve-3d; box-shadow: 1px 1px 1px rgba(0,0,0,0.25); cursor: pointer; /* for desktop */ } #board.level2 > div { height: 19%; } #board.level3 > div { height: 15%; } .back, .face, .back:after, .face:after { position: absolute; content: ""; top: 0; left: 0; right: 0; bottom: 0; border-radius: 3px; pointer-events: none; -webkit-backface-visibility: hidden; backface-visibility: hidden; } .back { border: 5px solid white; } .back:after { font-size: 2.5rem; line-height: 100%; background: 50% 50% no-repeat, 0 0 no-repeat #fff; font-style: normal; box-shadow: inset 1px 1px 0 currentcolor, inset −1px −1px 0 currentcolor, 1px 1px 1px rgba(0,0,0,0.1); color: rgb(119, 160, 215); background-image: url('data:image/svg+xml;utf8,<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><g><text xml:space="preserve" text-anchor="middle" font-family="serif" font-size="40" id="svg_1" y="30" x="20" stroke-width="0" stroke="rgb(119, 160, 215)" fill="rgb(119, 160, 215)">❀</text></g></svg>'), -webkit-linear-gradient(-15deg, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.025)); background-image: url('data:image/svg+xml;utf8,<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><g><text xml:space="preserve" text-anchor="middle" font-family="serif" font-size="40" id="svg_1" y="30" x="20" stroke-width="0" stroke="rgb(119, 160, 215)" fill="rgb(119, 160, 215)">❀</text></g></svg>'), linear-gradient(75deg, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.025)); -webkit-transform: rotatey(0deg); -webkit-transform: rotatey(0deg) translatez(0); transform: rotatey(0deg) transform: rotatey(0deg) translatez(0); } .face { -webkit-transform: rotatey(180deg); -ms-transform: rotatey(180deg); transform: rotatey(180deg); } #board > div.flipped { -webkit-transform: rotatey(180deg); -webkit-transform: rotatey(180deg) translatez(0); transform: rotatey(180deg); transform: rotatey(180deg) translatez(0); box-shadow: −1px 1px 1px rgba(0,0,0,0.25); }
In CubeeDoo, we use transforms to flip the card, and transitions
to do the flip in 250 milliseconds. The cards are shells with two
children: the face and back of the card. Because we are using CSS
classes to style the front of our cards, we can create all our card
faces with generated content. It is easier to maintain a single code
path. So, although we could have put the colors’ color scheme directly
on the face with background-color
,
and put the SVG shapes as a background-image
directly on the face <div>
, the number and second shapes
theme need to be generated content. To simplify, we put the SVG and
colors on the ::after
pseudoelement
generated content as well.
When the user taps or clicks on a card, the card flips. This is
done with transform: rotatey(180deg)
translatez(0);
on the card container. The issue is that the
HTML always has the back after the face in the
source order:
<div data-value="0" data-position="2"> <div class="face"></div> <div class="back"></div> </div>
Therefore, the back will always sit on top of the face. To hide
the back when the card is flipped and show the face instead, we add
backface-visibility: hidden;
. That
way, when the card is facing away from us, we do not see the elements
that are facing away from us (we’ll see the face and not the
back).
By adding the 3D transform of translateZ(0)
, we hardware accelerate it in
supportive devices, ensuring that the animation will be performed on the
GPU instead of the CPU. The reason we include four declarations:
-webkit-transform: rotatey(180deg); -webkit-transform: rotatey(180deg) translatez(0); transform: rotatey(180deg); transform: rotatey(180deg) translatez(0);
... is because not all browsers support 3D transforms. If a
browser doesn’t understand a line of CSS, it skips the whole
property/value declaration. Therefore, we first declare without translateZ()
for browsers that don’t
understand it, then with translateZ()
for browsers that do, both prefixed and unprefixed. That third
line—unprefixed yet targeting browsers not supporting 3D—will be
understood by browsers that support transforms but not 3D
transforms.
We also include transform-style:
preserve-3d
, as we want to ensure the front and the back of
the card—the card’s children—are in the same 3D space as the
card.
As we move from game level 1 to level 2 to level 3, the height of the cards gets smaller. Since our transition declaration of 0.25 s is on the default state, when the cards shrink, they do so over 0.25 s.
Generally, you don’t want to transition height
. Transitioning box model properties
causes the browser to reflow every frame, causing unnecessary reflows
and repaints. This is even more of an issue on mobile, and is
exacerbated by having a large number of DOM
nodes. A possible solution would be to transition a transform: scaleY
(0.8)
, as that would
maintain the cards’ width but only shrink the height, but this will
distort the shapes and number themes. We’ll leave this as is. The card
height changes only happen a maximum of two times per game. We were
careful not to have too many DOM nodes. This transition, with the
recalculation of all of the DOM nodes, while not optimal, works well
enough for this scenario.
A better solution would be to animate the scaling and switch out the class based on the animation end. As you’ll see next, animation is much more powerful than transitions. With animations, we would be able to change a class or otherwise add event listeners at the animation’s end.
As a counterpart to transitions and transforms, explicit animations provide a way to declare repeating animated effects with keyframes.
For simple transitions, when the starting value and ending value are
known, and only a single iteration of the animation is required, the
transition
properties may suffice for
your animating needs. If you need finer control of the intermediate values
of your animation, or you need to repeat your animation, the animation
properties of the CSS3 animation module, with keyframes, can be
used.
The animation properties include:
animation-name
The name you gave your keyframe animation definition, or a comma-separated
list of multiple animation names. The default value is none
, or no animation. Obviously,
therefore, you need to include an animation-name
if you want an element to
be animated.
animation-duration
The length of time in seconds or milliseconds an animation takes to complete one
cycle. The default value is 0s
,
which means that no visible animation will take place. In other
words, the animation-duration
property, with a value of greater than 0 seconds, is
required.
animation-timing-function
How the animation will progress over one cycle of its duration, taking the same values as
the transition-timing-function
.
Although the values are discreet, you can “animate” the animation-timing-function
in your keyframe
definitions. The default value is ease
.
animation-iteration-count
Number of times an animation cycle is played as an integer, or infinite
. The default value is a single
iteration.
animation-direction
Whether or not the animation should play in reverse on alternate cycles (alternate
) or not
(normal
).
animation-play-state
Defines whether the animation is running
or paused
. A paused animation displays the
current value of the animation in a static state. When a paused
animation is resumed, it restarts from the current value. The
default value is running
.
animation-delay
Defines when the animation will start. Interestingly, if the value is
negative, the animation will start partway through the animation.
For example, in a 10 second animation, if the animation-delay
is -4s
, the animation will start immediately
40% of the way through the first animation cycle.
animation-fill-mode
Defines what values are applied by the animation before the animation starts and after it ends. There are 4 possible values:
backwards
Applies the values defined in its 0% keyframe as soon as
the animation is applied, staying on the 0% keyframe through
the duration of the animation-delay
.
forwards
Maintains the values defined in the last keyframe after the animation is complete, until the animation style is removed from any selector targeting that node.
both
Implements both forwards
and
backwards
after the end and before
the start of the animation respectively.
none
The default value, does nothing, or removes any forwards
or backwards
type behaviors.
animation
The shorthand for the animation properties, space-separated values for
the animation-name
, animation-duration
, animation-timing-function
, animation-delay
, animation-iteration-count
, animation-direction
, and animation-fill-mode
properties. For a
multiple animation declaration, include a grouping for each
animation name, with each shorthand grouping separated by a
comma.
Keyframe animations involve setting the state of your elements at different
stages of an animation. Keyframes are specified using the @keyframes
rule.
The rule consists of the keyword @keyframes
, followed by an identifier giving a
name for the animation followed by a set of style rules encased in
curly braces ({}). You create the identifier[73] (name) of your animation. Do not quote the animation name
or identifier. This name is the name used as the value of the animation-name
property.
The keyframe selector for a keyframe style rule consists of a
comma-separated list of values. You can use percentage values or the
keywords from
and to
. For example:
@keyframes crazyText { from { font-size: 1em; } to { font-size: 2em; } }
In WebKit browsers, this would read as follows:[74]
@-webkit-keyframes crazyText { from { font-size: 1em; } to { font-size: 2em; } }
The keyword from
is equivalent to the value 0%. The keyword to
is
equivalent to the value 100%. Note that the percentage unit specifier
must be used. Therefore, “0” is an invalid keyframe selector.
If you need to define more than two points, more than the start and finish of an animation, having more granular control of the animation by defining keyframes for points in between, use percentages. For example:
@-prefix-keyframes rainbow {
0% {
background-color: red;
}
20% {
background-color: orange;
}
40% {
background-color: yellow;
}
60% {
background-color: green;
}
80% {
background-color: blue;
}
100% {
background-color: purple;
}
}
The keyframe selectors are used to specify the percentage along the duration of the animation that the keyframe represents. An analogy would be that the 20%, 40%, 60%, and so on are pseudoclasses of the transition duration (they’re not, but you can think of them that way). The keyframe is specified by the style rules (the code block of property values) declared on the keyframe selector.
The percentage or keyframe selector determines the placement of the keyframe in the animation. To make them easy to follow, I recommend keeping the keyframe selectors in order of the progression, from 0% to 100%, though this is not required.
Style blocks for keyframe selectors consist of properties and values. The animatable properties are listed in CSS3 Transforms. Properties that are unable to be animated are ignored in these rules.
To determine the set of keyframes, all of the values in selectors
are sorted in increasing order by time. Keyframe selectors do not
cascade; therefore an animation will never derive keyframes from more
than one keyframe selector. If there are any duplicates, then the last
keyframe selector specified inside the @keyframes
rule will be used to provide the
keyframe information for that time.
The animation engine will smoothly interpolate style between the
keyframe selectors. In these examples, shown in the online
chapter resources with an animation duration of 10 seconds with
linear animation timing function: at the 5 second mark, the crazyText
is at font-size: 1.5em
and the element with the
“rainbow” animation has a yellowish-green background.
We may have defined an animation, but we haven’t attached an animation to any
elements. Once we have defined an animation with @keyframes
, we apply it using, at minimum,
the two required properties: animation-name
and animation-duration
. The other related
animation properties are optional:
div { animation-name: crazyText; animation-duration: 1s; animation-iteration-count: 20; animation-direction: alternate; animation-delay: 5s; animation-fill-mode: both; }
The preceding rule attaches the “crazyText” animation, sets the duration to 1 second per iteration, makes it execute a total of 20 times with every other iteration play in reverse, waiting 5 seconds before commencing the first iteration.
The animation-fill-mode
of
both
means that when this <div>
is first rendered to the page,
the font-size
will be set to
1em
as per the 0%
or from
keyframe, prior to the 5 second delay
before the first iteration starts.
The font-size
will then
double in size over one second, then shrink back to 1em
over the next second because we set
animation-direction: alternate
. Had
we set animation-direction: normal
,
or omitted the animation-direction
property altogether, the font-size
would have doubled in size over one second, and then jumped back to
1em
before doubling over one second
again in the second iteration and all subsequent iterations.
When the animation has completed the 20 iterations, 25 seconds
after the animation was applied (20 one-second iterations plus a
five-second delay), the font-size
will remain at the last keyframe because we set animation-fill-mode
: both
. With animation-direction: alternate
, the font-size
will be 1em
. Had we omitted the animation-direction
property altogether or
set animation-direction: normal
,
then the last keyframe would have been at 2 em. The <div>
will remain at this font size
“forever” unless a font-size
declaration targeting this node overrides it.
This could have been written, with some padding and the rainbow animation added, with the shorthand:
div { padding: 20px; animation: crazyText 1s 20 5s alternate both, rainbow 4s infinite alternate; }
As with transitions, any property that has a discoverable midpoint can be animated. Two
exceptions are visibilility
and
animation-timing-function
. Neither
has a midpoint, but both can be added to a keyframe style
block.
If you include the animation-timing-function
in a keyframe, the
animation will switch from the default or current timing function to
the newly declared timing function at that point. Rarely used, this
can actually come in handy. For example, if you are creating a
bouncing ball, gravity dictates that the ball will get progressively
faster (or ease-in
) as it drops,
and progressively slower as it bounces up (or ease-out
):
@keyframes bouncing { 0% { bottom: 200px; left: 0; animation-timing-function: ease-in; } 40%, 70%, 90%{ animation-timing-function: ease-out; bottom: 0; } 55% { bottom: 50px; animation-timing-function: ease-in; } 80% { bottom: 25px; animation-timing-function: ease-in; } 95% { bottom: 10px; animation-timing-function: ease-in; } 100% { left: 110px; bottom: 0; animation-timing-function: ease-out; } }
There are several things to note about this code example (which is in the online chapter resources). We have three keyframes that have the exact same values—the 40%, 70%, and 90% keyframes—so we put them all on one line. We separated the keyframe selectors with commas just as we do normal CSS selectors.
We have more than one property being animated, but we don’t
declare all the values in every keyframe block. The left-to-right
motion is smooth, and therefore we only needed to declare that
property twice: in both the 0% and 100% blocks. We did move the ball
up and down numerous times with granular control, so unlike the
left
value, we declared the
bottom
value in every
keyframe.
We’ve also animated or changed the animation-timing-function
to make the bounce
look smooth and natural. Without it, our bouncing ball animation was
very jumpy. The animation-timing-function
is the only
animation property that can be included within keyframe
declarations.[75]
Generally when I think of HTML and animation, I think of animating a single node to a new position, or something dreadfully boring. One feature of CSS animations is creating character animations, like animating lemmings as they jump, or in this case float, off a cliff.
To create character animation using a sprite, we use the
animation-timing-function: step(x,
start)
value. The step values don’t move smoothly through
the keyframes. Rather, they break up the animation into the number of
steps declared and jump from one step to the next. To animate the
sprite in Figure 10-7,
we move the background image:
.lemming { height: 32px; width: 32px; background-image:url(lemming.gif); background-repeat: no-repeat; -webkit-animation: lemming 1s steps(8,end) alternate infinite; animation: lemming 1s steps(8,end) alternate infinite; } @-webkit-keyframes lemming { from { background-position: 0 0; } to { background-position: −256px 0; } } @keyframes lemming { from { background-position: 0 0; } to { background-position: −256px 0; } }
Note that the sprite is 256 px wide, so our background-position: 256px 0;
would normally
show no background image. With animation-timing-function
, you can declare
steps(x, start)
, which specifies
that the change in the property value happens at the start of each
step and steps(x, end)
, which means
the change in property comes at the end of the step.
Let’s use an animation with five steps. If you declare steps(5, start)
the jump will be at the
start of the step, so you’ll get five steps at the 20%, 40%, 60%, 80%,
and 100% marks—or basically the 0% really doesn’t show, because the
change in property from 0 to 20% happens at the start of the step, the
viewer sees the 20% mark from 0 to 20% time. If you declare steps(5, end)
, the jump to the next step
will be at the end of the interval, so it will appear to show the 0%,
20%, 40%, 60%, and 80% marks. This is why our last “step” is beyond
the width of the sprite since the 100% mark is never shown.
By creating a motion sprite and animating the background-position
, moving that sprite
using steps, you can create motion animations. The online
chapter resources have more examples of sprite animation.
In CubeeDoo, we have very simple animations. When a pair of cards is matched, the cards are animated as they disappear. And, when a score is a high score, it is highlighted with a blink-like animation in the high score area on larger screens:
#board > div.matched { -webkit-animation: fade 250ms both; animation: fade 250ms both; } #board > div.matched:nth-of-type(1) { -webkit-animation-delay: 250ms; animation-delay: 250ms; } @-webkit-keyframes fade { 0% { -webkit-transform: scale(1.0) rotatey(180deg) rotate(0) translatez(0); } 100% { -webkit-transform: scale(0) rotatey(180deg) rotate(720deg) translatez(0); } } @keyframes fade { 0% { transform: scale(1.0) rotatey(180deg) rotate(0) translatez(0); } 100% { transform: scale(0) rotatey(180deg) rotate(720deg) translatez(0); } } #highscores li.current { -webkit-animation: winner 500ms linear 8 alternate forwards; animation: winner 500ms linear 8 alternate forwards; } @-webkit-keyframes winner { 0% {background-color: hsla(74, 64%, 59%,1);} 100%{background-color: hsla(74, 64%, 59%,0)} } @keyframes winner { 0% {background-color: hsla(74, 64%, 59%,1);} 100%{background-color: hsla(74, 64%, 59%,0)} }
When the matched
class is
added to a card, the fade animation gets attached to that card. The
matched class is added to the two cards that have the flipped
class if the two flipped cards
match. Otherwise, if the two flipped cards aren’t a match, the
flipped
class is removed, and the
transition
described in our
sections on transitions occurs. We’ve included 3D transforms to ensure
that the animation is handled on the GPU instead of the CPU if
possible in the browser and device.
The fade
animation is a
misnomer. It doesn’t fade. It spins and shrinks over 250 ms. At the
end of the animation, the JavaScript removes both the flipped
and matched
classes, and resets and hides the
card by setting data-value="0"
attribute/value pair.
When the game ends, the list of high scores is regenerated. If
the current high score is one of the top 5 high scores, the class
current
gets added to the score as
it gets written to the page. We control adding and removing class
names with JavaScript, and we create and execute animations with CSS
purely based on the class. In the case of a high score, the winner
animation is executed, which is a
fading green background color that pulses on and off eight times: four
times from fully opaque to fully transparent, and four times from
fully transparent to fully opaque. This is controlled by declaring
animation-iterations: 8;
with
animation-direction: alternate;
in
the shorthand animation
property.
The current high score will remain with a green background until the
scores are redrawn. We didn’t put this animation in a 3D space as we
are doing a very simple repaint with no reflow, so it should perform
well by default.
CSS animations allow you to write declarative rules for animations, replacing lots of hard-to-maintain animation code in JavaScript.
Browsers are optimized to handle CSS animations. As such, CSS animations are more performant than JavaScript animations. If you can, always use CSS to animate instead of JavaScript. By using CSS, you allow the browser to optimize tweening and frame skipping, letting the browser optimize for performance.
While CSS animations are more performant than JavaScript animations, there are some drawbacks. Similar to JavaScript animations, CSS animations do suck up battery power. But CSS doesn’t occupy the CPU like JavaScript, so will generally be less jumpy.
CSS has last priority on the UI thread. This means that if you are
downloading a huge JavaScript file that takes eight seconds to load, the
page will not start animating during those eight seconds. While this may
not seem like a big issue, there is a quirk: while the animation won’t
start, the animation-delay
expires.
So, if you have 15 animations each starting a second apart using
animation delay of 0 s, 1 s, 2 s, and so on, the first eight animations
will all happen when the page had finally finished loading and
rendering, and the next seven animations will each occur when they were
timed to occur.
To resolve this issue, you can add a loaded
class to the document and base the
animations on being an element that is a descendant of the loaded
class.
Also, some properties perform better than others when animated. If you change the layout of the page forcing a reflow, the animation will not perform as well as when your animation is simply a repaint of an object. For example, the increase font size animation we did earlier is a stupid animation! The animation forces repeated reflows of the page: as font resizes, the element is resized. As the element is resized, the entire document is reflowed before the repaint. If we simply scaled the element using CSS transforms, and animated the transform, the browser would only be redrawing the animated element, which performs much better.
Remember that reflows are expensive and take more rendering time. To appear smooth, you want your animation frames to be fully drawn in under 16.67 ms.
There is also an animations API to capture events from the CSS
animations. animationStart
, animationEnd
, and animationIteration
are events that occur with
every iteration of the animation. We aren’t detailing this here, but
there are links to resources in the online chapter
resources.
We’re not done with CSS: we still have a few more features to discuss, which we do in Chapter 11.
[67] It is possible to drain battery animating certain CSS property values, but generally, browsers are well optimized for CSS.
[68] Vendor prefixing is needed: -webkit-
up through iOS6, Android,
BlackBerry 10, and Chrome through 25; -o-
for Opera up to 12; and -moz-
for Firefox up to 15. Support began in
IE with IE10 unprefixed. I recommend still including -webkit-
, but the other prefixes are
optional as browsers requiring them are already almost
obsolete.
[69] While explaining cubic Bézier is beyond the scope of this book, there is a good tool with which you can determine what other values you might need for your timing function. Another site, http://cubic-bezier.com, lets you compare the forward progress of one timing function against another.
[70] Depends on the number of vendor prefixes you include.
[71] Earlier versions of Internet Explorer support transitions via
filter:
progid:DXImageTransform.Microsoft.Matrix()
.
[72] Vendor prefixing is required for IE9, Firefox 3.5 to 15, Opera
through 12, and all versions of WebKit browsers. It is no longer
required beginning with Firefox 16, IE10, Opera 12.1, and Opera
Mobile 11. Opera Mini does not support transforms. All browsers that
support transform-origin
support
all of the 2D transform functions. Opera was prefixless in version
12.1, but the -webkit-
prefix
became required when they changed the browser engine away from
Presto.
[73] In CSS, IDENTs, or identifiers, including element names, classes, IDs, and keyframe animation names, and can contain only the characters [a-zA-Z0-9] and ISO 10646 characters U+00A0 and higher, plus the hyphen (-) and the underscore (_); they cannot start with a digit, two hyphens, or a hyphen followed by a digit. Identifiers are not quoted.
[74] Animations are supported in IE10, BlackBerry, Android, Chrome for Mobile, iOS, and all modern mobile browsers. They are not supported in Opera Mini, which is expected. They are still prefixed only in WebKit browsers and Boot2Gecko. Firefox dropped the prefix in Firefox 16, Opera with Opera 12.1, with Opera Mobile never having support for the prefixed version. IE began supporting animations with IE10, sans prefix.
[75] To learn more about how to code animations, the online chapter resources have links to an animation tutorial deck that shows every animation property “in action.”