Smooth state animations with animation-play-state

When a CSS animation is applied from the beginning of the page load, things are easy. You just use the animation property with appropriate parameters, and you’re done. However, what if the animation is applied on a certain state, e.g. :hover, :active, :focus or a JS-triggered class change?

A naïve approach would be to try something like this:

However, this means that when you hover out of the element, it abruptly snaps to its original state (no rotation). In many cases, it would be a more desirable to have it freeze in the last shown frame, until we hover over it again. To achieve that, we can apply the animation from the beginning, with animation-play-state: paused; and just change it on :hover to animation-play-state: running;. This is what happens then:

I figured this out when I was recently helping my good friend Julian with his one page website*. When you hover over the figure, it starts scrolling, but when you hover out of it, it doesn’t snap back to its original position, which would’ve looked awful.

*Beware it’s still a bit rough around the edges, e.g. the result has some rendering bugs on Firefox & IE plus some unsupported features messing it up (e.g. baseline-shift in SVG), but those are for another day as I had work to do and this ended up taking longer than the few hours I expected. Beyond the animation, you might want to explore the CSS-only buttons (see what I did there?) or the leather figure frame. Credits to Laura Kalbag for the tweed background & color scheme. I also experimented with SASS on this one and found it much smoother to work with than LESS, so I might stick with it for those cases where I need a preprocessor.

Screenshot

  • http://edmundojr.com/ Edmundo Junior

    Would be better if the animation do a turn and then stop. Is this possible?

    • http://lea.verou.me/ Lea Verou

      You mean to keep turning while you’re hovering and when you hover out do a full turn max, then stop? Can’t think of a way right now, but that doesn’t mean it doesn’t exist. Perhaps via changing animation-iteration-count from infinite to 1. But then it will make 1 spin in the beginning as well… Need to think more about this, thanks for the challenging question!

      • http://kodfabrik.se/ Pelle Wessman

        For simple animations adding a “transition” to the default state can at least prevent an abrubt end of the animation and make it almost look like the animation is completing itself?

        • http://lea.verou.me/ Lea Verou

          Obviously, if you can do what you want with a transition, it’s always better to do it with a transition.

  • Christian Heilmann

    This is very, very cool. One thing that always annoys me about this kind of animating though is that the boundary of the element rotates, so it is very simple to leave the element and stop the animation without wanting to. That’s why I always apply the hover to the parent element so that the interactive boundary box for the hover never changes, no matter what the transformation of the element is.

    • http://beben-koben.blogspot.com/ Beben Koben

      bravo CSS…see u script…hehe

      • http://lea.verou.me/ Lea Verou

        Huh?

        • http://beben-koben.blogspot.com/ Beben Koben

          bravo Lea Verou n Julian…hihi :D

  • http://www.pointlessrants.com Thomas Schultz

    Small observation, on my iPad mini the first example spins when putting my finger over it and holding but it’s pretty hard to interact with the text. The second example doesn’t spin, but I’m able to interact with the text.

    Kind of a cool progressive enhancement. Although if I wanted to copy the text from the first one on a desktop I’d be out of luck.

    • http://lea.verou.me/ Lea Verou

      The spinning is just a really obvious animation, for demonstration purposes. The point of this post is not spinning animations, neither should you have such a hover effect on a real website.

      • http://www.pointlessrants.com Thomas Schultz

        Totally agree, I was just observing how the interaction of the content in the div varied.

  • http://dpashk.com/ Dmitry Pashkevich

    AFAIK, It’s still kind of hacky to make the animation play in reverse when the mouse leaves the element, I wish there was an easier way…

  • Capotea

    Try stylus. All the features sass has and more, freedom in what kind of syntax you prefer, and holy shit transparent mixins! And it’s in JS! I honestly don’t know why people use sass when you have stylus.

  • Ariel Infante

    Thank you for the great tip.

  • Pingback: Tweet Parade (no.2 Jan 2014) - Best Articles of Last Week | gonzoblog

  • Pingback: Weekly Recap | visuelleGedanken.de

  • http://beben-koben.blogspot.com/ Beben Koben

    new property … hihihi
    ty ty

  • Ilincescu Mihai

    Maybe is a silly question but I don’t get it why on the website http://juliancheal.co.uk/ the body has it position: relative; ? I’m just curios :)

    • http://lea.verou.me/ Lea Verou

      Click on “Talks”, then toggle position:relative on the body through the dev tools and you’ll see why.

  • discoliam

    Would there be a way of adding an ease in when you start the animation again on hover? I’m playing with a similar rotation and can only get it to ease at the beginning/end of a full turn, not when you hover in/out.

  • Pingback: CSS Masters Israel | איך לשמור על STATE באנימציות של CSS3

  • Pingback: Gestire gli stati delle animazioni con animation-play-state | Laboratorio CSS

  • KyleBavender

    1. Thank you for this demonstration, Lea! Great technique and I plan to use. However…
    2. Chrome v33, OSX (current stable) is playing the animation at load despite “`animation-play-state: paused“`. Shoot.
    3. Further examination: My own usage plays at load, your dabblet example plays at load, but Julian’s image gets the pause. Now that’s all kinds of confusing.

    Do you have any ideas? Am I thinking about this wrong or missing something?

  • Lionel

    @LeaVerou:disqus once again thank you for your teachings! I just used this approach in a little project I made. CSShake: http://elrumordelaluz.github.io/csshake/