Our page is fairly static. Actually, it’s completely static. In Chapter 4 we learned a little about how to alter a form’s
appearance based on its state with the :invalid
and
:valid
pseudo-classes. But what about really moving
things around? What about changing the appearance of elements—rotating or
skewing them?
For years, web designers have relied on JavaScript for in-page animations, and the only way to display text on an angle was to use an image. This is far from ideal. Enter CSS3: without a line of JavaScript or a single JPEG, you can tilt, scale, move, and even flip your elements with ease.
Let’s see how it’s done.
Supported in Firefox 3.5+, Opera 10.5, WebKit since 3.2
(Chrome 1), and even Internet Explorer 9, the CSS3
transform
property lets you translate, rotate, scale,
or skew any element on the page. While some of these effects were possible
using previously existing CSS features (like relative and absolute
positioning), CSS3 gives you unprecedented control over many more aspects
of an element’s appearance.
We manipulate an element’s appearance using transform
functions. The value of the transform
property is one or more transform functions, separated by spaces, which
will be applied in the order they’re provided. In this book, we’ll cover
all the two-dimensional transform functions. WebKit also supports the
transformation of elements in 3D space—3D transforms—but that’s beyond the
scope of this book.
To illustrate how transforms work, we’ll be working on another advertisement block from The HTML5 Herald, shown in Figure 8.1.
Translation functions allow you to move elements left, right, up,
or down. These functions are similar to the behavior of
position: relative;
where you declare
top
and left
. When you employ a
translation function, you’re moving elements without impacting the flow
of the document.
Unlike position: relative
, which allows you to
position an element either against its current position or against a
parent or other ancestor, a translated element can only be moved
relative to its current position.
The translate(x,y)
function moves an element by
x
from the left, and y
from the
top:
-webkit-transform: translate(45px,-45px); -moz-transform: translate(45px,-45px); -ms-transform: translate(45px,-45px); -o-transform: translate(45px,-45px); transform: translate(45px,-45px);
If you only want to move an element vertically or horizontally,
you can use the translatex
or
translatey
functions:
-webkit-transform: translatex(45px); -moz-transform: translatex(45px); -ms-transform: translatex(45px); -o-transform: translatex(45px); transform: translatex(45px); -webkit-transform: translatey(-45px); -moz-transform: translatey(-45px); -ms-transform: translatey(-45px); -o-transform: translatey(-45px); transform: translatey(-45px);
For our ad, let’s say we want to move the word “dukes” over to the right when the user hovers over it, as if it had been punched by our mustachioed pugilist. In the markup, we have:
<h1>Put your <span>dukes</span> up sire</h1>
Let’s apply the style whenever the h1
is hovered over. This will make the effect
more likely to be stumbled across than if it was only triggered by
hovering over the span itself:
#ad3 h1:hover span { color: #484848; -webkit-transform: translateX(40px); -moz-transform: translateX(40px); -ms-transform: translateX(40px); -o-transform:translateX(40px); transform: translateX(40px); }
This works in most browsers, but you may have noticed that
WebKit’s not playing along. What gives? It turns out that WebKit will
only allow you to transform block-level elements; inline elements are off-limits. That’s easy enough to
fix—we’ll just add display: inline-block;
to our
span:
The result is shown in Figure 8.2.
It’s nice, but we can still do better! Let’s look at how we can scale our text to make it bigger as well.
The scale(x,y)
function scales an element by
the defined factors horizontally and vertically, respectively. If only
one value is provided, it will be used for both the x
and y
scaling. For example,
scale(1)
would leave the element the same size,
scale(2)
would double its proportions,
scale(0.5)
would halve them, and so on. Providing
different values will distort the element, as you’d expect:
-webkit-transform: scale(1.5,0.25); -moz-transform: scale(1.5,0.25); -ms-transform: scale(1.5,0.25); -o-transform: scale(1.5,0.25); transform: scale(1.5,0.25);
As with translate
, you can also use the
scalex(x)
or scaley(y)
functions.
These functions will scale only the horizontal dimensions, or only the
vertical dimensions. They are the same as scale(x,1)
and scale(1,y)
, respectively.
A scaled element will grow outwards from or shrink inwards towards
its center; in other words, the element’s center will stay in the same
place as its dimensions change. To change this default behavior, you can
include the transform-origin
property, which we’ll
be covering a bit later.
Let’s add a scale
transform to our span:
#ad3 h1:hover span { color: #484848; -webkit-transform: translateX(40px) scale(1.5); -moz-transform: translateX(40px) scale(1.5); -ms-transform: translateX(40px) scale(1.5); -o-transform: translateX(40px) scale(1.5); transform: translateX(40px) scale(1.5); }
Note that there’s no need to declare a new transform—you provide
it with a space-separated list of transform functions, so we just add
our scale
to the end of the list.
It’s also worth remembering that scaling, like translation, has no impact on the document flow. This means that if you scale inline text, text around it won’t reflow to accommodate it. Figure 8.3 shows an example of how this might be a problem. In cases like this, you might want to consider simply adjusting the element’s height, width, or font-size instead of using a scale transform. Changing those properties will change the space allocated to the element by the browser.
In our example, however, we want the text to pop out of the ad without reflowing the surrounding text, so the scale does exactly what we need it to do. Figure 8.4 shows what our hover state looks like with the scale added to the existing translation.
The rotate()
function rotates an element around
the point of origin (as with scale
, by default this
is the element’s center), by a specified angle value. Generally, angles
are declared in degrees, with positive degrees moving clockwise and
negative moving counter-clockwise. In addition to degrees, values can be
provided in grads, radians, or turns—but we’ll just be sticking with
degrees.
Let’s add a rotate
transform to our
“dukes”:
#ad3 h1:hover span { color: #484848; -webkit-transform:rotate(10deg) translateX(40px) scale(1.5); -moz-transform:rotate(10deg) translateX(40px) scale(1.5); -ms-transform:rotate(10deg) translateX(40px) scale(1.5); -o-transform:rotate(10deg) translateX(40px) scale(1.5); transform:rotate(10deg) translateX(40px) scale(1.5); }
We’re rotating our span
by ten
degrees clockwise—adding to the effect of text that has just been dealt
a powerful uppercut. We are declaring the rotation
before the translate
so that
it’s applied first—remember that transforms are applied in the order provided. Sometimes
this will make no difference, but if an effect is behaving differently
to what you’d like, it’s worth playing with the order of your
transforms.
The final transformed text is shown in Figure 8.5.
There’s one more type of transform we’re yet to visit. It won’t be used on The HTML5 Herald, but let’s take a look anyway.
The skew(x,y)
function specifies a skew along
the X and Y axes. As you’d expect, the x
specifies
the skew on the X axis, and the y
specifies the skew
on the Y axis. If the second parameter is omitted, the
skew
will only occur on the X axis:
-webkit-transform: skew(15deg, 4deg); -moz-transform: skew(15deg, 4deg); -ms-transform: skew(15deg, 4deg); -o-transform: skew(15deg, 4deg); transform: skew(15deg, 4deg);
Applying the above styles to a heading, for example, results in the skew shown in Figure 8.6.
As with translate
and scale
,
there are axis-specific versions of the skew transform:
skewx()
and skewy()
.
As we hinted at earlier, you can control the origin from which
your transforms are applied. This is done using the
transform-origin
property. It has the same syntax
as the background-position
property, and defaults
to the center of the object (so that scales and rotations will be around
the center of the box by default).
Let’s say you were transforming a circle. Because the default
transform-origin
is the center of the circle,
applying a rotate
transform to a circle would have no
visible effect—a circle rotated 90 degrees still looks exactly the same
as it did before being rotated. However, if you gave your circle a
transform-origin
of 10% 10%
, you
would notice the circle’s rotation, as Figure 8.7
illustrates.
The
transform-origin
property is
supported with vendor prefixes in WebKit, Firefox, and Opera:
-webkit-transform-origin: 0 0; -moz-transform-origin: 0 0; -o-transform-origin: 0 0; transform-origin: 0 0;
While CSS3 transforms are unsupported in IE6, IE7, or IE8, you can
mimic these effects with other CSS properties, including filters. To
“translate,” use position: relative;
, and
top
and left
values:
.translate { position: relative; top: 200px; left: 200px; }
You can also scale an element by altering its width and height. Remember, though, that while transformed elements still take up the space that they did before being scaled, altering a width or height alters the space allocated for the element and can affect the layout.
You can even use filters to rotate an element in Internet Explorer, but it’s ugly:
.rotate { transform: rotate(15deg); filter: progid:DXImageTransform.Microsoft.Matrix( sizingMethod='auto expand', M11=0.9659258262890683, M12=-0.25881904510252074, M21=0.25881904510252074, M22=0.9659258262890683); -ms-filter: "progid:DXImageTransform.Microsoft.Matrix( M11=0.9659258262890683, M12=-0.25881904510252074, M21=0.25881904510252074, M22=0.9659258262890683, sizingMethod='auto expand')"; zoom: 1; }
This filter’s syntax isn’t worth going into here. If you want to
rotate an element in Internet Explorer, go to http://css3please.com/ for cross-browser code for a given
rotation. Just edit any of the rotation
values, and
the other versions will be updated accordingly.
As much fun as it’s been to have a feature work in IE9, it’s time to again leave that browser behind. While Opera, Firefox, and WebKit all support CSS transitions, IE is once again left in the dust.
Transitions allow the values of CSS properties to change over time, essentially providing simple animations. For example, if a link changes color on hover, you can have it gradually fade from one color to the other, instead of a sudden change. Likewise, you can animate any of the transforms we’ve just seen, so that your pages feel more dynamic.
Animation has certainly been possible for some time with JavaScript, but native CSS transitions require much less processing on the client side, so they’ll generally appear smoother. Especially on mobile devices with limited computing power, this can be a lifesaver.
CSS transitions are declared along with the regular styles on an element. Whenever the target properties change, the browser will apply the transition. Most often, the change will be due to different styles applied to a hover state, for example. However, transitions will work equally well if the property in question is changed using JavaScript. This is significant: rather than writing out an animation in JavaScript, you can simply switch a property value and rely on the browser to do all the heavy lifting.
Here are the steps to create a simple transition using only CSS:
Declare the original state of the element in the default style declaration.
Declare the final state of your transitioned element; for example, in a hover state.
Include the transition functions in your default style
declaration, using a few different properties:
transition-property
,
transition-duration
,
transition-timing-function
, and
transition-delay
. We’ll look at each of these and
how they work shortly.
The important point to note is that the transition is declared in
the default state. Currently, the transition functions need to include the
vendor prefixes -webkit-
, -o-
, and
-moz-
, for WebKit, Opera, and Firefox,
respectively.
This may be a lot to grasp, so let’s go over the various transition values. As we go, we’ll apply a transition to the transforms we added to our ad in the last section, so that the word “dukes” moves smoothly into its new position when hovered.
The transition-property
lists the CSS
properties of the element that should be transitioned. Properties that
can be made to transition include background, border, and box model
properties. You can transition font sizes and weights, but not font
families. The W3C last updated the list of properties that can be
transitioned in August 2010:
background-color
and
background-position
border-color
,
border-spacing
, and
border-width
bottom
, top
,
left
, and right
clip
color
crop
font-size
and
font-weight
height
and
width
letter-spacing
line-height
margin
max-height
,
max-width
, min-height
, and
min-width
opacity
outline-color
,
outline-offset
, and
outline-width
padding
text-indent
text-shadow
vertical-align
visibility
word-spacing
z-index
More properties are available to transition in some browsers, including the transform functions, but they’re not (yet) in the proposed specifications. Note also that not all browsers support transitions on all the above properties at the time of writing.
You can provide any number of CSS properties to the
transition-property
declaration, separated by
commas. Alternatively, you can use the keyword all
to
indicate that every supported property should be animated.
In the case of our ad, we’ll apply the transition to the
transform
property:
#ad2 h1 span { -webkit-transition-property: -webkit-transform; -moz-transition-property: -moz-transform; -o-transition-property: -o-transform; transition-property: transform; }
Note that we need to specify the prefixed forms of properties—you
can’t animate transform
in a browser that only
understands -moz-transform
, for example.
Because the list of properties that can transition is in flux, be
careful what you include: it’s possible that a property that doesn’t
animate at the time you’re writing your page eventually will, so be
selective in the properties you specify, and only use
all
if you really want to animate every
property.
So far these styles will have no effect; that’s because we still need to specify the duration of the transition.
The transition-duration
property sets how
long the transition will take. You can specify this either in seconds
(s
) or milliseconds (ms
). We’d
like our animation to be fairly quick, so we’ll specify 0.2 seconds, or
200 milliseconds:
-webkit-transition-duration: 0.2s; -moz-transition-duration: 0.2s; -o-transition-duration: 0.2s; transition-duration: 0.2s;
With those styles in place, our span
will transition on hover. Notice that the
“reverse” transition also takes place over the same duration—the element
returns to its previous position.
Although transitions are only supported in some
browsers, the fact that they’re declared separately from the
properties that are changing means that those changes will still be
apparent in browsers without support for transitions. Those browsers
will still apply the :hover
(or other) state just
fine, except that the changes will happen instantly rather than being
transitioned over time.
The transition-timing-function
lets you
control the pace of the transition in even more granular detail. Do you
want your animation to start off slow and get faster, start off fast and
end slower, advance at an even keel, or some other variation? You can
specify one of the key terms ease
,
linear
, ease-in
,
ease-out
, or ease-in-out
. The best
way to familiarize yourself with them is to play around and try them
all. Most often, one will just feel right for the effect you’re aiming
to create. Remember to set a relatively long
transition-duration
when testing timing
functions—if it’s too fast, you won’t be able to tell the
difference.
In addition to those five key terms, you can also describe your
timing function more precisely using the
cubic-bezier()
function. It accepts
four numeric parameters; for example, linear
is the
same as cubic-bezier(0.0, 0.0, 1.0, 1.0)
. If you’ve
taken six years of calculus, the method of writing a cubic Bézier
function might make sense; otherwise, it’s likely you’ll want to stick
to the five basic timing functions. You can also look at online tools
that let you play with different values, such as http://www.netzgesta.de/dev/cubic-bezier-timing-function.html.
For our transition, we’ll use ease-out
:
-webkit-transition-timing-function: ease-out; -moz-transition-timing-function: ease-out; -o-transition-timing-function: ease-out; transition-timing-function: ease-out;
This makes the transition fast to start with, becoming slower as it progresses. Of course, with a 0.2 second duration, the difference is barely perceptible.
Finally, by using the transition-delay
property, it’s also possible to introduce a delay before the animation
begins. Normally, a transition begins immediately, so the default is
0
. Include the number of milliseconds
(ms
) or seconds (s
) to delay the
transition:
-webkit-transition-delay: 250ms; -moz-transition-delay: 250ms; -o-transition-delay: 250ms; transition-delay: 250ms;
Interestingly, a negative time delay that is less than the duration of the entire transition will cause it to start immediately, but it will start partway through the animation. For example, if you have a delay of -500ms on a 2s transition, the transition will start a quarter of the way through, and will last 1.5 seconds. This might be used to create some interesting effects, so it’s worth being aware of.
With four transition properties and three vendor prefixes, you
could wind up with 16 lines of CSS for a single transition. Fortunately,
as with other properties, there’s a shorthand available. The
transition
property is shorthand for the four
transition functions described above. Let’s take another look at our
transition so far:
#ad2 h1 span { -webkit-transition-property: -webkit-transform, color; -moz-transition-property: -moz-transform, color; -o-transition-property: -o-transform, color; transition-property: transform, color; -webkit-transition-duration: 0.2s; -moz-transition-duration: 0.2s; -o-transition-duration: 0.2s; transition-duration: 0.2s; -webkit-transition-timing-function: ease-out; -moz-transition-timing-function: ease-out; -o-transition-timing-function: ease-out; transition-timing-function: ease-out; }
Now let’s combine all those values into a shorthand declaration:
#ad2 h1 span { -webkit-transition: -webkit-transform 0.2s ease-out; -moz-transition: -moz-transform 0.2s ease-out; -o-transition: -o-transform 0.2s ease-out; transition: transform 0.2s ease-out; }
Note that order of the values is important and must be as follows (though you don’t need to specify all four values):
transition-property
transition-duration
transition-function
transition-delay
The transition properties allow for multiple transitions in one call. For example, if we want to change the color at the same time as changing the rotation and size, we can.
Let’s say instead of just transitioning the rotation, we
transition the text’s color
property as well. We’d
have to first include a color
property in the
transitioned style declaration, and then either the
color
property in the
transition-property
value list, or use the key term
all
:
transition-property: transform, color; transition-duration: 0.2s; transition-timing-function: ease-out;
You can also specify different durations and timing functions for
each property being animated. Simply include each value in a
comma-separated list, using the same order as in your
transition-property
:
transition-property: transform, color; transition-duration: 0.2s, 0.1s; transition-timing-function: ease-out, linear;
The above properties will apply an ease-out
transition over 0.2 seconds to the transform
, but a
linear
transition over 0.1 seconds to the
color
.
It’s possible to specify multiple transitions when using the
shorthand transition
property also. In this case,
specify all the values for each transition together, and separate each
transition with commas:
transition: color 0.2s ease-out, transform 0.2s ease-out;
If you want to change both properties at the same rate and delay,
you can include both property names or, since you are transitioning all
the properties listed in the hover state anyway, you can employ the
all
keyword.
When using the all
keyword, all the properties
transition at the same rate, speed, and delay:
-webkit-transition: all 0.2s ease-out; -moz-transition: all 0.2s ease-out; -o-transition: all 0.2s ease-out; transition: all 0.2s ease-out;
If you don’t want all your properties to transition at the same
rate, or if you just want a select few 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.
Transitions animate elements over time; however, they’re limited in what they can do. You can define starting and ending states, but there’s no fine-grained control over any intermediate states. CSS animations, unlike transitions, allow you to control each step of an animation via keyframes. If you’ve ever worked with Flash, you’re likely very familiar with the concept of keyframes; if not, don’t worry, it’s fairly straightforward. A keyframe is a snapshot that defines a starting or end point of any smooth transition. With CSS transitions, we’re essentially limited to defining the first and last keyframes. CSS animations allow us to add any number of keyframes in between, to guide our animation in more complex ways.
At the time of this writing, only WebKit supports CSS animation. This means that support on the desktop is limited, but support on mobile devices is fairly good, as the default browsers on iOS and Android both run on WebKit. As we’ve already mentioned, the lack of powerful processors on many mobile devices make CSS animations a great alternative to weighty, CPU-intensive JavaScript animation.
To animate an element in CSS, you first create a named animation, then attach it to an element in that element’s property declaration block. Animations in themselves don’t do anything; in order to animate an element, you will need to associate the animation with that element.
To create an animation, use the @keyframes
rule—or @-webkit-keyframes
for current WebKit
implementations—followed by a name of your choosing, which will serve as
the identifier for the animation. Then, you can specify your
keyframes.
For an animation called myAnimation
, the
@keyframes
rule would look like this:
@-webkit-keyframes 'myAnimation'{ /* put animation keyframes here */ }
Each keyframe
looks like its own nested CSS declaration block. Instead of a selector,
though, you use the keywords from
or
to
, a percentage value, or a comma-separated list of
percentage values. This value specifies how far along the animation the
keyframe is located.
Inside each keyframe, include the desired properties and values. Between each keyframe, values will be smoothly interpolated by the browser’s animation engine.
Keyframes can be specified in any order; it’s the percentage values, rather than the order of the declarations, that determine the sequence of keyframes in the animation.
Here are a few simple animations:
@-webkit-keyframes 'appear' { 0% { opacity: 0; } 100% { opacity: 1; } } @-webkit-keyframes 'disappear' { to { opacity: 0; } from { opacity: 1; } } @-webkit-keyframes 'appearDisappear' { 0%, 100% { opacity: 0; } 20%, 80% { opacity: 1; } }
The last animation is worth paying extra attention to:
we’ve applied the same styles to 0% and 100%, and to 20% and 80%. In
this case, it means the element will start out invisible
(opacity: 0;
), fade in to visible by 20% of the way
through the duration, remain visible until 80%, then fade out.
We’ve created three animations, but they aren’t attached to any elements. Once we have defined an animation, the next step is to apply it to one or more elements using the various animation properties.
The animation properties supported by WebKit are as follows, with
the -webkit-
vendor prefix.
This property is used to attach an animation (defined using the
@keyframes
syntax previously) to an element:
-webkit-animation-name: 'appear';
Note that the quotes around the animation name in both the
property value and the @keyframe
selector are
optional. We recommend including them to keep your styles as legible
as possible, and to avoid conflicts.
The animation-duration
property defines the
length of time, in seconds or milliseconds, an animation takes to
complete one iteration (all the way through, from 0% to 100%):
-webkit-animation-duration: 300ms;
Like the transition-timing-function
property, the animation-timing-function
determines how the animation will progress over its duration. The
options are the same as for
transition-timing-function
:
ease
, linear
,
ease-in
, ease-out
,
ease-in-out
, or
cubic-bezier
:
-webkit-animation-timing-function: linear;
This property lets you define how many times the animation will
play through. The value is generally an integer, but you can also use
numbers with decimal points (in which case, the animation will end
partway through a run), or the value infinite
for
endlessly repeating animations. If omitted, it will default to
1
, in which case the animation will occur only
once:
-webkit-animation-iteration-count: infinite;
When the animation iterates, you can use the
animation-direction
property with the value
alternate
to make every other iteration play the
animation backwards. For example, in a bouncing ball animation, you
could provide keyframes for the falling ball, and then use
animation-direction: alternate;
to reverse it on
every second play through:
-webkit-animation-direction: alternate;
The default is normal
, so the animation will
play forwards on each iteration.
When animations are played in reverse, timing functions are also
reversed; for example, ease-in
becomes
ease-out
.
Used to define how many milliseconds or seconds to wait before the browser begins the animation:
-webkit-animation-delay: 15s;
The animation-fill-mode
property defines
what happens before the animation begins and after the animation
concludes. By default, an animation won’t affect property values
outside of its runs, but with
animation-fill-mode
, we can override this default
behavior. We tell the animation to “sit and wait” on the first
keyframe until the animation starts, or stop on the last keyframe
without reverting to the original values at the conclusion of the
animation, or both.
The available values are none
,
forwards
, backwards
, or
both
. The default is none
, in
which case the animation proceeds and ends as expected, reverting to
the initial keyframes when the animation completes its final
iteration. When set to forwards
, the animation
continues to apply the values of the last keyframes after the
animation ends. When set to backwards
, the
animation’s initial keyframes are applied as soon as the animation
style is applied to an element. As you’d expect,
both
applies both the backwards
and forwards
effects:
-webkit-animation-fill-mode: forwards;
The animation-play-state
property defines
whether the animation is running
or
paused
. A paused animation displays the current
state of the animation statically. When a paused animation is resumed,
it restarts from the current position. This provides a simple way to
control CSS animations from your JavaScript.
Fortunately, there’s a shorthand for all these animation
properties. The animation
property takes as its
value a space-separated list of values for the longhand
animation-name
,
animation-duration
,
animation-timing-function
,
animation-delay
,
animation-iteration-count
,
animation-direction
, and
animation-fill-mode
properties:
.verbose { -webkit-animation-name: 'appear'; -webkit-animation-duration: 300ms; -webkit-animation-timing-function: ease-in; -webkit-animation-iteration-count: 1; -webkit-animation-direction: alternate; -webkit-animation-delay: 5s; s-webkit-animation-fill-mode: backwards; } /* shorthand */ .concise { -webkit-animation: 'appear' 300ms ease-in 1 alternate 5s ↵backwards; }
To declare multiple animations on an element, include a grouping for each animation name, with each shorthand grouping separated by a comma. For example:
.target { -webkit-animation: 'animationOne' 300ms ease-in 0s backwards, 'animationTwo' 600ms ease-out 1s forwards; }
With transforms, transitions, and animations, our site is looking more dynamic. Remember the old maxim, though: just because you can, doesn’t mean you should. Animations were aplenty on the Web in the late nineties; a lot of us remember flashing banners and scrolling marquees, and to some extent, that problem still exists today. Use animations and transitions where it makes sense, enhancing the user experience—and skip it everywhere else.
We still have a few lessons to learn in CSS3 to make our website
look more like an old-time newspaper. In the next chapter, we’ll learn
about creating columns without relying on float
, and
how to include fancy fonts that aren’t installed by default on our users’
computers.