Menu

Full-page Animations Using CSS

August 16th, 2012

Internet Explorer 9 introduced support for CSS 2D Transforms. Internet Explorer
10 Developer Preview added support for
CSS 3D Transforms
and CSS Animations.
By tapping the power of your GPU and running asynchronously from regular JavaScript,
these IE10 features provide a more performant and flexible alternative to traditional
script-based animations for Web content.

In previous blog posts, we covered
CSS 3D Transforms
as well as
CSS Animations and Transitions
. In this post, we introduce a more “unconventional”
use case for these technologies by describing the concept of “full-page animations”
that can be used during the navigation process to add fluidity and continuity to
browsing. Our target is to achieve a seamless browsing experience in which content
smoothly appears into view when the user visits a page and transitions away when
he clicks on a link or performs a relevant action.

These effects can be accomplished by transforming the HTML <body>
element using CSS Animations. However, this use case presents some considerations
that we felt were worthy of discussion, such as the effect of layout and sizing
on transforming <body>, as well as how to appropriately time
page navigations so that they mesh properly with our animations.

The code samples in this post use unprefixed CSS markup as supported by IE10 Release
Preview; other browsers may require vendor prefixes for the CSS Animations and CSS
Transforms properties used.

Transforming a Page’s Entire Content

CSS Transforms are defined on the stylistic properties of an HTML DOM Element. For
example, the markup for rotating an element 45 degrees along its Z axis would look
like this:

#element {

transform: rotateZ(45deg);

}

Attaching a transform to the <body> element of your HTML document
works exactly the same way. So performing in order to declaratively add the same
effect to your document’s <body> you could do something like
this:

body {

transform: rotateZ(45deg);

}

Let’s look at a before-and-after shot of a page when applying a transform to the
body element:

For three dimensional transformations, the CSS Transforms specification defines
the perspective property that can be specified on the parent of the
element that we transforming. When transforming the <body> element
of your content, it has to be applied to the <html> element that
resides above it in the DOM hierarchy. Doing so is straightforward:

html {

perspective: 500px;

}

Combining this with a rotateY(45deg) transform on the <body>
element yields the following result:

We can manipulate the transform-origin property on the body element
for interesting results. Let’s look at a couple of examples:

body {

transform-origin:
50%
100%;

transform: rotateX(45deg);

}

The above markup sets a rotation along X for the body element while shifting the
origin of rotations to the bottom of the element using transform-origin.
Effectively this rotates the document’s contents “into” the screen like this:

Screen shot showing applying transform: rotateX(45deg) and transform-origin:  50% 100% to <body>

We can also manipulate the perspective-origin property on the root
element of our document to achieve an off-axis projection effect. Changing the style
for <html> to:

html {

perspective: 500px;

perspective-origin:
90%
50%;

}

Our page now looks like this:

Screen shot showing applying perspective: 500px and perspective-origin: 90% 50% to the <html> element

By using CSS Transforms, we can easily manipulate the visual appearance of the entirety
of our page’s content. Since the usual layout and sizing rules still apply, some
transforms on the body element (particularly ones that use percentage values or
rely on the transform-origin property) can result in different visual
effects depending on the content of our page. Recall our previous rotateX(45deg)
example with transform-origin set to 50% 100%.

Below you can see the results before and after the transform is applied.

Screen shot highlighting the difference in scroll bars before and after the application of a transform under perspective projection

Notice how the content does not actually pivot on the bottom of the window but rather
at some point outside of the viewport. This is expected behavior for CSS Transforms:
the <body> is laid out normally, then it is rotated along its
bottom edge that is somewhere off screen. You will also notice that the actual foot
print of the content has expanded (take a look at the scroll bars in the “after”
picture) in order to accommodate the transformed content (the fact that we are using
perspective projection makes this effect even more pronounced).

So how do we deal with arbitrarily sized content when applying transforms to our
body element? Custom tailoring all content in order to ensure that the size of the
body does not expand more than a certain amount may be unrealistic. Instead, we
can use a simple HTML/CSS pattern that allows us to fix the size of the body element
to that of the browser window and append content inside a wrapper <div>.
The following markup achieves just that:

html, body
{

width: 100%;

height: 100%;

min-width: 100%;

max-width: 100%;

padding: 0;

margin: 0;

overflow: hidden;

}

 

#Wrapper {

position: absolute;

width: 100%;

height: 100%;

overflow: scroll;

}

The illustration below shows what happens when a page is scrolled vertically and
we apply a rotateY(45deg) transform to the <body>
element of our document directly (left) and using the wrapper pattern (right):

Screen shot showing what happens when a page is scrolled vertically and a rotateY(45deg) transform is applied to the <body> element under perspective projection, with and without a wrapper CSS/HTML pattern

The direct application of the transform results in a skewed visual result due to
the off-axis projection (since we are no longer looking at the “center” of the body
element). Using the wrapper pattern ensures that the <html> element’s
perspective-origin property (50% 50% by default) will
always be correctly centered with relation to the <body> element,
giving us a pleasant visual effect.

By utilizing the above pattern and setting up CSS Transforms with percentage values
whenever possible, we can affect our <body> element in consistent
ways, regardless of the size of its contents.

From Transforms to Animations

Having sorted out the intricacies of applying CSS Transforms to the <body>
element, CSS Animations are the next step. By following the principles described
above, we can create animations that bring our Web content into view (or remove
it from view) in interesting ways.

Consider this basic @keyframes rule:

@keyframes rotateInLeft {

from {

transform-origin:
0%
0%;

transform: rotateY(180deg);

}

to {

transform-origin:
0%
0%;

transform: rotateY(0deg);

}

}

When applied to an element, this animation will cause it to rotate on its left side.
When applied to a <body> element that uses our wrapper pattern
the visual result is more interesting. The document will actually rotate from outside
of the visible area of the browser window and into full view:

Three screen shots in sequence showing the affect of applying the "rotateInLeft" animation

Similarly, we can compose animations that fluidly remove our Web content from view.
For example, if we wanted our page to disappear into the distance while rotating,
we could use something like this:

@keyframes whirlOut {

to {

transform: scale(0)
rotateZ(1260deg);

}

}

With the visual result being:

Three screen shots in sequence showing the affect of applying the "whirlOut" animation

Since we can use the full power of CSS Animations to affect the entirety of our
Web content, we have a lot of flexibility in terms of generating these page effects
(and we are certainly not limited to just using CSS Transforms). But once we have
composed the effects that we want to apply to our content, how do we cause them
to trigger during the page navigation process?

Attaching Animations to <body>

Our goal is to use trigger animations at strategic times during the browser experience
in order to give the appearance of content transitioning into view when a page loads
and out of view when the user clicks on a link.

The first intuitive place to add an animation to the body element would be the
onload
JavaScript event. As it turns out however, adding an animation
when onload fires is actually too late. This event actually triggers
when the entirety of the content in our page has finished loading (including any
images or other bandwidth-intensive resources). Attaching an animation to onload
on a bandwidth-intensive page would result in our content displaying “normally,”
followed by the animation triggering and re-bringing the content into view. Not
exactly the effect that we were aiming for.

Alternatively, we could utilize the DOMContentLoaded event that triggers
when the browser has finished parsing the DOM structure of our content (but potentially
before resources have finished loading). The IE Test Drive
DOMContentLoaded demo
illustrates the difference between these
two events. However, in cases of complex Web content, a modern browser may choose
to perform “progressive” rendering, displaying the page before the entirety of the
DOM tree has been loaded. In these situations, the visual result would be similar
to the onload scenario.

The optimal place to set up an animation that transitions our page content in view
is inline at the top of the <body> element. This ensures that
the animation will commence right as the content is being rendered (and that the
starting position of the content will be that of the from keyframe
of our selected animation). A pleasant side effect of this approach is that the
animation may actually mask any progressive rendering, re-layout or resource loading
that can occur with complex content.

Setting up the animations that transition our content out of view is also interesting.
One could assume that we could attach an onclick handler to all elements
of interest in our content (for instance all <a> tags) and just
set the relevant animation properties (animation-name, animation-duration,
etc.) in the callback function. However, if we do not actually delay the
navigation from happening, we will not see our expected fluid transition.

This is a good opportunity to utilize the
animation events
described in the CSS Animations specification. In particular,
we can use the animationend event to detect when the animation has
completed and then trigger a navigation (by setting window.location.href,
for instance). Thus our onclick will trigger the “remove-from-view”
animation and register a handler for animationend on <body>
that will ensure that the navigation event occurs.

Live Demo Available

We’ve created a demonstration
and tutorial on bringing pages alive with CSS Transforms & Animations

that provide depth and examples beyond what we’ve been able to show here. The tutorial
itself utilizes full page animations during page navigation that work in
Internet Explorer 10 on Windows 8
as well as recent versions of Chrome and
Firefox..

To simply enjoy the page-to-page animations, step through the pages of the tutorial using the “Continue to …” links in the lower right corner of each page.

At the end of the tutorial we provide some additional guidance and sample code on
how to incorporate these animations with your own Web content.

Conclusion

CSS Transforms and CSS Animations are two powerful feature-sets that enable richer
and more immersive Web experiences. This blog post outlined the considerations of
using CSS Transforms and CSS Animations to bring the entirety of your Web content
to life. With a small amount of effort you can create Web pages (even static ones)
that provide a fluid and almost app-like navigation experience.

—Charilaos “Harris” Papadopoulos, Program Manager Intern, Internet Explorer Graphics