Hostingheaderbarlogoj
Join InMotion Hosting for $3.49/mo & get a year on Tuts+ FREE (worth $180). Start today.
Advertisement

CSS3 Animations: the Hiccups and Bugs You'll Want to Avoid

by

CSS3 animation is pretty darn cool. It lets us give hardware accelerated life to our previously dull websites. However, there are some major pitfalls and practices you should be aware of - let's dig in.

The browser support for CSS3 animation is hotting up - Mozilla Firefox has joined WebKit in full support, and Internet Explorer 10 as well as Opera 12 have promised future support. This means we can start using them without fear today. However, this thriving support doesn't come without its problems.


Not the Androids We're Looking For

Being a Webkit born-and-bred property, you'd expect CSS animation to have full support across all Webkit browsers. Unfortunately, it's not quite that simple. The browser built into Android devices does support animations - to some degree. The problem is, it will only support animations with a single changing property.

So let's say we wanted to work with my animations framework, Animate.css, to make a super cool website. Most of the animations such as bounceInLeft will work great on the latest Chrome, Safari, Firefox and iOS Safari. But on an Android device, the user won't see a single animated element. The animation runs, but the element disappears as soon as it's finished.

This strange quirk means that the only animations we can run on Android devices are single property ones, such as fadeIn and bounce. Unfortunately, it's unclear whether or not Android 4.0 will provide better support - hopefully it will.

The Animate.css home page shows absolutely nothing on Android devices

Until we see greater support from these Android devices, your best bet is to provide Javascript fallbacks or less complex animations. But how can you tell your site not to use your super cool animations on these devices? Using Modernizr won't help here - it'll detect that the browser supports animations. Luckily, we can check the user agent, and serve up an alternative stylesheet or script. In PHP, it's as easy as this:

$ua = strtolower($_SERVER['HTTP_USER_AGENT']);
if(stripos($ua,'android') !== false) { // && stripos($ua,'mobile') !== false) {
	$css = "droid-style.css";
} else {
	$css = "style.css";
}

That snippet will load up droid-style.css on Android devices, meaning we can keep our fancy-pants animations for the browsers that can use them. Excellent!


For 2D, Press 3D

iOS devices like iPhone and iPad have great support for CSS3 animations, with full hardware acceleration - but only if it's a 3D transform. If the animation doesn't involve a 3D transformation, it'll be accelerated purely by the software, which can result in laggy animations.

To get full hardware acceleration on animations, you need to activate the 3D rendering engine. That's as easy as adding this rule to your CSS:

.container {
    -webkit-transform: translateZ(0);
}

Great! But why not apply this rule to the body? Well, for some reason, activating the 3D engine plays with the layout of the page a little bit. Fixed or absolutely positioned elements will shift slightly with this applied to the body.

As with all the things in the CSS3 world, this clever trick comes at a cost. Not a hefty one, but a cost nonetheless. Take a look at this scientific comparison:

Antialiasing screws up with the active 3d rendering engine

It takes a keen eye to see it, but activating the 3D rendering engine makes the antialiasing of text greyscale - this results in slightly sharper text, and it's particularly noticeable on extremely small or extremely large text sizes. Like I said, it's no biggie, but it's still a bug. Adding -webkit-font-smoothing: antialiased; can help reduce the problem slightly on Webkit browsers.


The Problem With Transforms

Let's imagine you need to move an element from one spot to another, without it affecting the layout of the page. translate is your best friend here - the translate property, as defined by the W3C:

...does not affect the flow of the content surrounding the transformed element. However, the value of the overflow area takes into account transformed elements.

See that? It doesn't affect the flow of surrounding content. But it does affect the overflow of the document. This means that if you move an element way off the page (which is the case for a few animations on Animate.css), it'll cause scrollbars.

Browsers treat this behavior a little differently amongst themselves. Safari and Chrome will create scrollbars, but then once the transformation is complete and the element's opacity is dropped to zero, it'll remove the scrollbars, as if the element has been removed. Firefox, on the other hand, creates persistent scrollbars that will stay until the element is removed from the document or set to display: none. The best way to work with these transforms is to use a little Javascript magic. If you know the length of your animation, you should remove the element from the DOM once it's finished the animation. Alternatively, you could watch for when the animation is complete with some clever Javascript:

var element = document.getElementById("#box");
element.addEventListener("webkitAnimationEnd", function(){
    this.style.webkitAnimationName = "";
}, false);
document.getElementById("#button").onclick = function(){
    element.style.webkitAnimationName = "shake";
    // you'll probably want to preventDefault here.
};

This little snippet actually comes from a Stack Overflow thread. It's a good one, so bookmark it!


Fighting the Flash

If you're writing your own CSS animation, you might notice something strange on Safari, Chrome, and iOS devices. Just before the animation runs, the animated element in question will flash in and out of visibility just before it starts.

The reason behind this phenomenon is unclear, but the fix is pretty simple. Simply adding the -webkit-backface-visibility: hidden; rule to your CSS should help prevent the problem. Apply it to the container of the element, like so:

.container {
	-webkit-backface-visibility: hidden;
}

It boils down to another hardware acceleration problem - using this property simply kicks the acceleration into gear. You could also use things like -webkit-perspective: 1000; or other 3D properties.


Pseudo Elements and Z-index

Here's another couple of unfortunate truths with CSS animations and transitions. You can't use animations or transitions on pseudo elements. This fact is somewhat poorly documented on the web, but it's an important one. Let's say, for instance, you wanted an :after pseudo element to fade into visibility when you hover over it's parent. Unfortunately, it'll snap between states instead of transitioning gracefully. As far as we know, this is unusual behavior: there are bug reports filed for Chrome and Webkit - and Firefox actually supports these transitions. You can keep up with the current state of support for transitions & animations on pseudo elements over at CSS-tricks.

However, it is worth noting that if you apply transitions or animations to an element that has pseudo elements, those transitions will be carried through to the pseudo elements.

And now for another kick in the teeth. Transitions and animations reset the z-index of an object. If you have some nicely stacked objects you want to animate, they'll have their stacking order reset. It's an unfortunate truth and again, not one that many people talk about.


Realistic Motion

I might be getting into pretty deep water here, but you can make your animations a lot more realistic by playing with the timing functions. If you just leave the default timing functions for the animations (ease), you can end up with some pretty nice effects. However, if you're trying to recreate a particular motion pattern in CSS, it's important to learn the differences between the different timing functions. Let's take the 'hinge' animation from Animate.css as an example.

The idea behind this animation was to have the element drop from its current position, swing from a single corner for a little while, then drop off completely - kind of like a broken sign. Here's the catch - the animation will require a variety of timing functions to make it realistic. Think about this motion in real life; when the sign drops and starts swinging, it'll start off slowly, then speed up, then slow back down at it's peak. However, when it drops off completely, it should drop quite suddenly and speed up as it falls. Sounds like we need both ease-in-out and ease-in.

It's actually pretty easy. Inside your keyframes, you can declare the different timing functions - like this:

@keyframes hinge {
	0% {
		animation-timing-function: ease-in-out;
		transform: rotate(-120deg);
		transform-origin: top left;
	}

	25% {
		animation-timing-function: ease-in-out;
		transform: rotate(70deg);
		transform-origin: top left;
	}

	50% {
		animation-timing-function: ease-in-out;
		transform: rotate(-120deg);
		transform-origin: top left;
	}

	100% {
		animation-timing-function: ease-in;
		transform: rotate(0deg) translateY(200%);
	}
}

By using a variety of timing functions, rather than just the default one, we have greater control over how the animation plays. If you run the hinge animation without the custom timing functions, you can see how strange it appears. when writing your animations, compare them to real life instances of the same motion. Slow it down in your mind, and think about how each one is timed.


Don't Design Like it's 1999

This might be the most important thing you take away, so listen up. I firmly believe that CSS animations are there to be used sparingly for added interaction, and nothing more. Clever folks like Anthony Calzadilla are making some crazy cool stuff with CSS animations, but in my opinion, in the real world these animations should be few and far apart.

Take this page as an example. Everything here (except the obligatory animated gif) is animated using CSS. It's an extreme example, but you can see why I'm worried about the overuse of these things.

Wow. CSS3 gone bad.

The same goes for all the CSS3 properties. Transitions, shadows, and gradients should be used with subtlety. I think the landing page for Owlr makes a pretty good example. Submitting an invalid email or an empty form will create a validation error with a couple of non-essential animations to draw attention.

Nicely done.

In his book CSS3 For Web Designers, (which is a great read) Dan Cederholm talks a lot about the "experience layer". He stresses the point that all the CSS3 values demonstrated in his book are for an added layer of experience only - the effects added are non essential, and should always degrade gracefully in browsers that don't fully support them. Remember this rule, and you'll become more powerful than you could possibly imagine. Or something like that.


Performance Issues

Most computers have more than enough power to run CSS3 animations and transitions with no problem - however, the more you try to pump into your CSS, the more it will affect performance.

Take for example a simple animation which moves a div from left to right infinitely. Simple stuff. Let's make this box a little more interesting - give it a box shadow, text shadow, maybe a gradient... all these rules will affect performance of the page. If you gave your div a box shadow with a ridiculous blur radius (I'm talking into the hundreds here), you'll notice a severe drop in performance. The browser is working so hard trying to figure out how it should render the pixels making up the box shadow that it starts hogging resources needed by the animation, or even the rest of the page - simple tasks such as scrolling can become painfully slow. Keep visual enhancements to a minimum, particularly shadows.

Hardware acceleration helps prevent this problem, but it's still an issue to be aware of. Not all your users will have the luxury of hardware acceleration, and software rendered graphics can take their toll on the computer.


Conclusion

Hopefully you're more aware of some of the pitfalls in CSS3 animation and all its cross browser quirks than you were before. But don't forget that these CSS3 features are still very exciting and available to use today. Support is ever-growing, and it signals a world where we can move away from Javascript for visual effects - which is a great thing if you ask me.

I'll finish with this simple reminder:

Your content and functionality takes priority over everything else. If you find yourself using any CSS3 features, be sure that your site will function the same way for every user without them as well.

Advertisement