CodeinWP CodeinWP

Replacing Subtle Flash Animations with CSS3

Replacing Subtle Flash Animations with CSS3I often come across instances of animations and other effects that look like perfect candidates to be switched to equivalent CSS3-based solution. I recently came across a website called 84Colors, which belongs to a freelance designer named Cristiana Bardeanu.

On that site, you’ll notice there are a number of subtle animations that integrate nicely with her overall brand and design.

Her animations are done using Flash. I thought it would be fun to grab some of those elements and create a CSS3-based page that mimicked what those Flash-based objects do.

Now, before anyone claims that I’m ripping off her graphics and insulting her development skills, please know that I contacted Cristiana via email and she was more than happy with the idea, and gave me permission to do this. The fact is, I love her site’s design — which she says is about three years old. And as you can see from her portfolio, she’s quite talented, so be sure to check that out for some inspiration.

The Demo

The demo page is pretty simple. I didn’t rip out her entire web page, I just used Photoshop to hack up some of the animated elements and give them some new drop shadows and exported them as individual transparent PNGs. Then I just started writing the CSS3 animation code to get a similar effect to what she’s doing on 84colors.

I didn’t spend hours trying to replicate her animations down to the last pixel; that’s not the point of this experiment. The point of this is to show how easy it is to use CSS3 animations for simpler effects that can easily degrade gracefully in non-supporting browsers.

In fact, some of my animations are slightly exaggerated in comparison to hers, just to make them a little more visible.

Animating the Leaves and the Flower

The code for each of the leaf elements and the flower is pretty much the same, with some small variations. I won’t print all the code here, just one example, with some explanations.

(Note: The code shown here uses the standard syntax, for brevity. So it may not work in all browsers as is.)

.deco {
  position: absolute;
}

.leaf-1 {
  left: 670px;
  animation: leaf1 2s ease infinite alternate 0;
  top: -32px;
}

@keyframes leaf1 {

  from {
  }

  to {
    transform: rotate(11deg) 
               translateX(-6px);
  }
}

The “deco” class is applied to all the animated elements. They are all absolutely positioned so this saves some code using OOCSS principles.

the leaf-1 element infinitely animates and alternates the animation (meaning it goes forwards, then backwards, then forwards again, and so on). The animation itself is a rotation combined with a slight translation (similar to regular CSS positioning). The reason I’m using translate instead of position is because evidently translate has better performance.

You’ll also note that the “from” keyframe is empty. Technically, I could omit this keyframe and (if I’m not mistaken) the browser should still understand the animation because it will automatically construct the “from” keyframe when it is omitted. I could also just repeat the same initial styles in the “from” keyframe, but that would just be redundant.

So that’s the first leaf waving in the wind. There are six other separate leaf images, plus a flower, on the demo page that have very similar code.

Animating the Squirrel

The squirrel is divided into three parts: The head, the tail, and a blinking eyelid. Here’s the code for the main part of the squirrel:

.squirrel {
  top: 450px;
  left: 142px;
  z-index: 8;
  animation: squirrel .7s ease 1 normal 2s forwards;
}

@keyframes squirrel {
  from {
  }

  to {
    transform: translateX(-90px);
  }
}

The squirrel is given a z-index setting to make sure he’s below the box that he peeks out from behind. In this case, the animation (unlike the leaves) is not repeating infinitely; instead it’s set to “normal” with an iteration-count of “1”, so that it doesn’t repeat.

I’ve also set the animation-fill-mode to “forwards”, so that the squirrel stays where he is, and I’ve delayed his entrance by two seconds.

Now here’s the code for the tail:

.tail {
  top: 402px;
  left: 175px;
  z-index: 5;
  animation: tailrotate 1s ease infinite alternate 2.5s,
             tailslide .7s ease 1 normal 2s forwards;
  transform: rotate(0deg);
  transform-origin: bottom right;
}
    
@keyframes tailrotate {

  from {
  }

  to {
    transform: rotate(-3deg);
  }
  
}

@keyframes tailslide {

  from {
  }

  to {
    left: 85px;
  }
  
}

the z-index positions the tail behind the main part of the squirrel and the tail has two separate animations occurring. One of the animations is called “tailrotate” and the other is “tailslide”.

The “tailslide” animation slides the tail in along with the rest of the squirrel. The “tailrotate” gives it a subtle rotation, to give it some natural tail-wagging movement, similar to what’s being done with the leaves and the flower.

The “tailrotate” animation is delayed by 2.5 seconds, to allow for the entrance to occur first. The tail’s entrance is obviously not infinitely looped, whereas the “wagging” is. Also, the “tailslide” has the “forwards” value to ensure it stays where it is, just like the rest of the squirrel.

Finally, I had to use positioning for the slide-in. This is because I couldn’t figure out an easy way to have multiple animations on the same element that use two different transforms. Remember that multiple transforms on one object are space separated using a single property declaration. If I use transform for the tail slide, then the rotation will not take effect, because the slide transform nullifies the rotation.

You’ll also notice that the squirrel’s eye blinks every few seconds. The code for the eyelid looks like this:

.eyelid-holder {
  overflow: hidden;
  width: 8px;
  height: 8px;
  top: 480px;
  left: 90px;
  z-index: 10;
}

.eyelid {
  transform: translate(-10px, -10px);
  animation: eyelid 4s linear infinite normal 2.5s;
}

@keyframes eyelid {

  from {
  }

  79% {
    transform: translate(-10px, -10px);
  }

  80% {
    transform: translate(0, 0);
  }
  
  90% {
    transform: translate(-10px, -10px);
  }
  
  to {
    transform: translate(-10px, -10px);
  }
  
}

The eyelid is placed inside of an eyelid “holder”. It’s stacked above the squirrel using z-index, so it appears on top. The holder has its overflow set to “hidden”. The eyelid itself is moved so that it’s outside the holder’s boundaries (and thus is hidden), then it animates infinitely.

You’ll notice in this case that the animation has more than just two keyframes. The 79% keyframe is used to maintain the initial hidden state of the eyelid up to that point. Then at 80% the eyelid becomes visible, and finally hides again at 90%. I probably could have avoided having the 90% state the same as the “to” state by adjusting the duration of the animation, but this does the job just the same.

What About Non-Supporting Browsers?

Browser support for keyframe animations is limited to Chrome, Safari, and Firefox 5+. The demo page includes code for both WebKit and Mozilla, along with the standard syntax for future proofing. Unfortunately, although the squirrel animates fine in Firefox, Mozilla doesn’t seem to support transforms in keyframe animations, so you won’t see any movement in the leaves in Firefox. Thanks to Divya, the Firefox issue is fixed.

The elements that are animated in this example are purely decorative. So because the animations are quite subtle, you have a few different options for non-supporting browsers.

You could use Modernizr to detect keyframe animation support, then use the classes that Modernizr creates to position the elements to be fully visible and static when animation is not available. You could also fall back to a jQuery-based animation.

In my example, I’m fixing the squirrel’s position with Modernizr’s CSS hook, like this:

.no-cssanimations .tail {
  left: 85px;
}
    
.no-cssanimations .squirrel {
  left: 52px;
}
    
.no-cssanimations .eyelid-holder {
  display: none;
}

The eyelid holder is hidden to ensure that the squirrel doesn’t look like he’s snoozing in non-supporting browsers.

You also have the option to create all these animated elements in your HTML using JavaScript. You’ll notice that each of them is placed on the page using a simple <img> tag. By injecting them into the DOM using JavaScript, you ensure that they don’t form part of the original accessible markup, thus keeping the markup clean.

In any case, less-capable user agents will see an acceptable version of the page, so there won’t be anything that’s mangled or incomplete.

Conclusion

While Flash will continue to be used for many complex animations, I think this type of thing is a perfect candidate for CSS3 keyframe animations.

So thanks to Cristiana of 84Colors for being a good sport and permitting me to use her website as a case study.

18 Responses

  1. Divya says:

    You do not need a ‘from’ as per the spec (and in later Safari, Chrome, Moz). You also can combine the 79%, 80% and 100% (‘to’) in one go given only one property is changing. ‘from’ is equivalent to 0% and ‘to’ to 100%.

    Also missing are moz prefixes :)

    • I have an explanation for all of that! :)

      I did mention about the optional “from” in the article, but left it in anyhow.

      Regarding the 79%, 80%: The reason I’ve done that is because, first of all, on each iteration of the eyelid’s appearance, I want there to be a delay. If I combine the 79/80/90/100 then I’m not going to get the blinking eye. Also, keep in mind that the eyelid blink needs to take place over a set duration. The duration I chose is 4 seconds. The eyelid itself blinks at 80-90%. The rest is just delay. The delay has to occur on each iteration, so that’s what that does. The “animation-delay” property doesn’t happen on each iteration, only at the beginning of the first iteration.

      Finally, my demo includes full code for WebKit and Mozilla. For brevity here, I only included WebKit. I thought I had mentioned that in the article, but I’ll add a note if not. Thanks.

      • Divya says:

        Er by combine I mean this 79%, 90%, 100% { -webkit-transform: translate(-10px, -10px); } and 80% { -webkit-transform: translate(0, 0); }

        Also for brevity you can remove vendor-prefixes all together. It is cleaner and would force people to look for all the prefixes that would work!

        • Ah I see what you’re saying. Interesting, I don’t think I knew that was possible. Maybe I did but forgot about it. Good to know. I was aware that the order of the keyframes doesn’t matter, so yeah, that’s only logical. I’m going to leave it how it is though, just to be more explicit.

          Yeah, I guess I could do it without the prefixes at all, but I think it’s better to have something that’s copy-and-paste ready and just works, so I’ll just leave it like this for now. Also, the transforms don’t seem to work in Firefox within the keyframes, so it’s limited there anyhow.

          Thanks for reminding me about the keyframe ordering.

          • Divya says:

            You need to add the ‘s’ to the delay that you set to 0. The spec sez it should not be unitless. But webkit seems to render without units w/e.

            http://jsfiddle.net/nimbu/2f9nK/3/ works in moz.

            Also using inline images for what should ideally be background seems wrong, empty elements might be a better idea (with bg images) and hopefully you can animate pseudo-elements soon.

          • Attention everyone! Divya is the greatest. :)

            Nice, I updated it and now it works the same in Firefox. That’s awesome. Good find.

            Yeah, inline images are lame, I know. It was more of a proof of concept. Plus, as I mentioned, any way I did it they would be injected via JavaScript.

            But to be honest, are empty elements any better? Maybe slightly, but it’s still no different if you’re injecting via script right? Maybe there is an accessibility difference, I’m not sure. But if it’s the same accessibility, then it shouldn’t really matter.

          • Divya says:

            Well, you can inject empty elements and it would be cleaner and faster :)

          • Paul Irish says:

            IMO all copy-paste code should be correct and complete. At HTML5 Rocks we leave off prefixes often for brevity, under the understanding its up to the user to fill in the gaps. By publishing single-vendor copy-paste code you’re okay with single-vendor css3 websites. :(

          • Then it just makes the code in the article bloated. But I see your point. In other areas (where each browser needs one line of code), it’s much easier to do that.

            I guess your point here is that, unlike the debate over semantics, MACHINES *DO* CARE. :)

  2. geek says:

    finally , i found the solution to make a blinking eye effect. thanks impressive webs :)

    make more article about css3 and html ya..

  3. IT Mitică says:

    Both Cristiana’s and Louis’s work, very inspiring.

    While I understand this is just a proof of concept, a play to better understand and use CSS3, my personal stance on CSS3 animations is “I don’t dig’em”.

    My probable approach: static graphics elements initially. JavaScript animation added for them as a progressive enhancement.

    My approach may be looked at as being a wrong move, “CSS is for design, JS for behaviour”. I believe we’re past this milestone a few years back now.

    CSS should do less and better. Animations are nice, layout is better. That’s real design. Let’s stick with that.

    Javascript is for whatever I can do to improve my page, while not making it impossible to use without it.

    The word “animation” screams progressive enhancement and programmatic code, not style. And it’s applicable today, cross-browser.

  4. PeHaa says:

    Thanks a lot for this article, you might see my approach to this subject, my tutorial : http://pehaa.com/2011/08/postcard-from-paris-css3-keyframes-animations-in-use/
    where I animate also mulltiple backgrounds.

  5. Weboide says:

    It does work in Firefox 6.

  6. This is awesome and a great example of how to work with CSS3 but I think it’s important that readers of such articles be aware of just how these “little touches” can impact a site in huge ways. For example, this simple demonstration throws my CPU into a fury — and for what? A blinking squirrel?

    These are all great techniques and I cannot wait for browser vendors to continue optimizing their rendering engines for animation but know that if you put too many of these effects on a page you can impact the UX in horrible ways.

  7. Timmy says:

    Thanks for this. Btw, I found this great animation of moving clouds (all made in CSS3) and replicating the parallax effect! I loved it :) http://css-creations.com/item/68/moving-clouds-animation

Leave a Reply

Comment Rules: Please use a real name or alias. Keywords are not allowed in the "name" field and deep URLs are not allowed in the "Website" field. If you use keywords or deep URLs, your comment or URL will be removed. No foul language, please. Thank you for cooperating.

Markdown in use! Use `backticks` for inline code snippets and triple backticks at start and end for code blocks. You can also indent a code block four spaces. And no need to escape HTML, just type it correctly but make sure it's inside code delimeters (backticks or triple backticks).