Animating Indefinite Dimensions with CSS3
If you, like many, have started using CSS3 transitions to create slick, simple
animations in your app then you might have noticed a problem when trying to
animate properties such as width
and height
: they only work with
specific values.
While this isn't always an issue, if you're trying to build something like a collapsible content block it can be a big problem: your content is variable height, so you can't set a specific value on it. I was running into this particular wall yesterday and so put it out to Twitter. I got this in response:
@mbleigh maybe try animating max-height
— Andy Ogzewalla (@andyogzewalla) June 21, 2012
A ha! There's a clever idea for a workaround. Rather than animate the property
that we don't/can't know in advance, we'll animate the max-height
property
instead. But does it work? Yes! Well, yes with a but. The animation works
fine but it will not be the exact length you expect: if your content is
100px
tall and you animate the max height to 200px
the animation will
execute in half the time. This can also mess with easing functions so isn't a
perfect solution.
Performing the Animation
So how do we set this up? Let's take a simple "collapsible content block" example. Here's a simple example of how to do it:
http://jsfiddle.net/mbleigh/fsrGk/embedded/
Pixel Perfect
So what if we want to make our animations pixel-perfect? Well, unfortunately
we're going to have to rely on Javascript. When the page loads we will need to
iterate through all collapsibles and set the height of each one to its
automatically determined height. We will also need to change the animations to
height instead of max-height and set the !important
flag on the zero height.
Here's the result:
http://jsfiddle.net/mbleigh/f6Xby/embedded/
Note that this implementation requires that we re-calculate the height if the
content inside the collapsible changes at any point. This brittleness is the
main reason that, even though the animation is smoother, I'm tempted to use
the max-height
trick. It requires less intrusive Javascript and can continue
to work even if the content changes.