#135: Three Ways to Animate SVG

Animating SVG is a bit unique in that there are three distinctly different ways you can approach animating it.

1. Animating with CSS @keyframes

SVG elements can be targeted and styled with CSS. Meaning, you can apply animation through @keyframes. Like this:

<svg viewBox="0 0 127.9 178.4">
  <path id="left-leg" d="M37.6,138.8c0 ... " />
</svg>
.left-leg {
  fill: orange;
  animation: dance 2s infinite alternate;
}
@keyframes dance {
  100% {
    transform: rotate(3deg);
  }
}

You might choose animating this way if...

  • The animation is fairly simple.
  • You only need to animate properties that CSS can animate.
  • You already know and are comfortable with CSS animations.

2. Animating with SMIL

There is a syntax for animations built right into SVG. Here's a very simple example:

<svg viewBox="0 0 127.9 178.4">
  <path d="M37.6,138.8c0 ... ">
    <animate attributeName="fill" dur="5000ms" to="#f06d06" fill="freeze" />  
  </path>
</svg>

Here's a big tutorial on all that is SMIL.

You might choose animating this way if...

  • You need to animate properties that CSS can't, like the shape itself.
  • You need other SMIL specific features, like beginning an animation when another ends without manually syncing durations/delays. Or interaction stuff, like beginning an animation on a click.

3. Animating with JavaScript

With JavaScript, you have access to things like requestAnimationFrame (or other loops), so you can animate just by way of rapidly changing property values. There are also frameworks out there for working with SVG that typically have animation stuff built in. Or animation frameworks that work with SVG. Like SnapSVG, GreenSock, SVG.js, or Velocity.js.

Here's an example with SnapSVG:

<svg id="robot" viewBox="0 0 127.9 178.4">
  <circle id="left-pupil" cx="34.4" cy="15.4" r="4.8" />
</svg>
var s = Snap("#robot");
var leftPupil = s.select("#left-pupil");

leftPupil.animate({
  r: 50,
  fill: "lightgreen"
}, 1000);

You might choose animating this way if...

  • You're working in JavaScript anyway, perhaps your animation has to do with data you receive with JSON or the like.
  • You need JavaScript anyway, because you need the logic or math or something else really only possible there.
  • You're interested in the JavaScript solving some bugs for you.
  • The scope of your animation is rather large/complicated and you need the abstraction and organization JavaScript can provide.

Demo

See the Pen Three Ways to Animate SVG by Chris Coyier (@chriscoyier) on CodePen.

Doesn't how you ultimately use the SVG affect your options?

It does. If you're using SVG-as-<img>, you won't be able to use use CSS animations from another stylesheet. But, you're SMIL animations will work, in some browsers (at the time of this writing, Chrome yes, Firefox no). I wouldn't be surprised if embedded CSS in SVG files work or will work one day. JavaScript, probably not.

If you're using SVG in a CSS background-image, I imagine it's a similar story as above.

If you use inline <svg>, all the possibilities are open to you.

If you're using SVG through an object or iframe, you would need to embed the scripts/styles right in the SVG for it to work.

Comments

  1. User Avatar
    Michael Musgrove
    Permalink to comment#

    Awesome stuff! Thanks!

  2. User Avatar
    Clare Suereth
    Permalink to comment#

    Hi,

    I just tried to recreate this code within my WordPress website, and I am able to get the JavaScript working, but I’d really like to be able to use the CSS animation. I cannot get that to work at all. I copied all of the CSS and the SVG from the example. In WP, I also remove all of the line breaks from the SVG file. The file itself shows up fine, but the CSS is not working. Any ideas??

    Thank you!

    • User Avatar
      Clare Suereth
      Permalink to comment#

      Edit: I got the working and not the JS. I also am using Chrome. I checked it in FireFox and Safari and it is not working with CSS in either of them. I also tried with !important on all of the CSS and that didn’t help. I’m not that familiar with @keyframes. Maybe I need to add another step for WP?

      Here is my CSS:

      #right-eye {
        animation: redeye 2s infinite alternate!important;
      -webkit-animation: redeye 2s infinite alternate!important; /* Safari 4+ <em>/
        -moz-animation:    redeye 2s infinite alternate!important; /</em> Fx 5+ <em>/
        -o-animation:      redeye 2s infinite alternate!important; /</em> Opera 12+ */
        animation:         redeye 2s infinite alternate!important; 
      }
      
      @keyframes redeye {
        to {
          -webkit-fill: #FF0000!important;
      fill: #FF0000!important;
        } }
      
    • User Avatar
      torzborz
      Permalink to comment#

      You have to prefix your keyframes too:

      @-webkit-keyframes redeye {
       to {
        -webkit-fill: #F00;
        fill: #F00;
       }
      }
      @keyframes redeye {
       to {
        fill: #F00;
       }
      }
      
    • User Avatar
      Clare Suereth
      Permalink to comment#

      That did it! Thank you!!!!

    • User Avatar
      MF

      Hi Clare . can you do it in css

  3. User Avatar
    Ana
    Permalink to comment#

    Another reason for using JS animations would be IE support, it’s the only way that works. Also the reasons listed for SMIL apply to JS as well.

  4. User Avatar
    Ihatetomatoes
    Permalink to comment#

    Thanks for sharing Chris.

    Here’s an SVG animation (Google Chrome experiment) I did using Greensock.

    https://ihatetomatoes.net/svg-christmas/

    I think it’s currently the best tool for more complex timelines.

    I hope we’ll see SVGs transform-origin with percentage values implemented more consistent across the browsers. That seems to be the missing piece.

    • User Avatar
      Rob Levin
      Permalink to comment#

      ihatetomatoes, did you compare it to snap.svg? Thoughts? Thanks for sharing your animation. Must have been quite a few lines of code there ;-]

    • User Avatar
      Ihatetomatoes
      Permalink to comment#

      @Rob – I’ll be honest I haven’t looked at snap.svg in a lot of detail, I’m sure it’s great and useful library.

      There is also a plugin which makes Snap.svg including polygon and path shape tweening to work with Greensock, but again I haven’t tried it yet, just found it now.

    • User Avatar
      Rob Levin
      Permalink to comment#

      Hey Peter, looks like you’re xmas animation got featured in css-weekly (you probably already know..but if not now you do).

      Plans to do a “code walk through” and/or is the code available for perusal? Having done a bunch of snap.svg animations recently I’m really curious what your code looked like for something that’s a bit more complicated (mine are simple 3 or 4 step sequences, whereas yours is defintely closer to a realistic ad campaign [in terms of number of sequences/sophistication]).

    • User Avatar
      Rob Levin
      Permalink to comment#

      @ihatetomatoes Argh, apologies ^Peter^Petr

    • User Avatar
      Ihatetomatoes
      Permalink to comment#

      Thanks for the update Rob, I didn’t know about the css-weekly mention.

      I will be deconstructing the page in my future articles on websitedeconstructions.com and my blog.

      In the meantime here is a simplified Codepen demo with some of the GreenSock timelines.

      Can you share some of your snap.svg animations?

    • User Avatar
      Rob Levin
      Permalink to comment#

      Thanks Petr. Wow, that .to chaining syntax is definitely quite nice. I’ve been meaning to write a sequence method, but I might make a .to instead :)

      I made a deferAnimation which allowed me to start one animation mid-way of another..it’s just a wrapper around setTimeout: https://github.com/adobe-webplatform/Snap.svg/issues/335

      My experiments are sort of similar to some of the codedrops ones, and I’m writing a tutorial but it won’t be for a bit (especially with holidays coming up and all). If I get a spare cycle to throw up a pen I’ll come back and drop it here. In meantime (just so you see how potentially verbose the code is), here’s an animation for toggling between a hamburger icon to and ‘X’ where I hide the middle rectangle and just morph the paths (warning: ugliness follows!):

      var initHamburgerAnimation = function() {
              loadSvg('.icon-hamburger', 48, 'svg/hamburger.svg', function (fragment, svg, $button) {
                  var group = fragment.select('g');
      
                  var rectTop = group.select('#rect-top');
                  var rectMiddle = group.select('#rect-middle');
                  var rectBottom = group.select('#rect-bottom');
                  svg.append(rectTop);
                  svg.append(rectMiddle);
                  svg.append(rectBottom);
      
                  $button.on(clickEvent, function(e) {
                      var isOpened = $('.icon-hamburger').data('is-open');
                      $('.icon-hamburger').data('is-open', !isOpened);
      
                      if (isOpened) {
                          rectTop.stop().animate({path: 'M44,17H4v-4h40V17z'}, 200, mina.bounce);
                          rectMiddle.stop().animate({opacity: 1}, 200, mina.linear);
                          rectBottom.stop().animate({path: 'M44,35H4v-4h40V35z'}, 200, mina.bounce);
                      } else {
                          rectTop.stop().animate({path: 'M39.7,11.3L11.4,39.6l-2.8-2.8L36.8,8.4L39.7,11.3z'}, 200, mina.easeout);
                          rectMiddle.stop().animate({opacity: 0}, 100, mina.linear);
                          rectBottom.stop().animate({path: 'M36.8,39.6L8.6,11.3l2.8-2.8l28.3,28.3L36.8,39.6z'}, 200, mina.easeout);
                      }
                  });
              });
          };
      

      Syntactically, loadSvg is my own custom helper that just sets up the constructor stuff (48 is just for my 0 0 48 48 viewBox). The two libs (greensock / snap.svg) seem to have familiar if not fairly similar interfaces (I was able to read your codepen pretty comfortably). Nice to know there’s some nice lib options to choose from!

    • User Avatar
      Rob Levin
      Permalink to comment#

      Just looked at your app.min.js using prettyprint chrome (until my eyes got dizzy lol) and I definitely need to practice fully utilizing the transform origin! I did some of this making eyeballs look to each side, but I love the way your effects look by messing around with that. Very neat. Fwiw, I don’t have this big background doing Flash/AS3 so I’ve very much been learning to think in animation sequences. Curious, do you have a past in Flash or animations?

    • User Avatar
      Ihatetomatoes
      Permalink to comment#

      No wonder your eyes got dizzy, the code is uglified which makes all variables a, b, c etc.

      I did study multimedia and did a bit of Flash/AS, but that’s almost 10 years ago. Since then all I’ve been doing for living is front-end work (CSS/HTML/JS).

      And animations? You just need to play around with things and see what feels natural.

    • User Avatar
      Rob Levin
      Permalink to comment#

      Yah, that’s what I’ve been doing, a tweak here, a tweak there. It’s pretty exciting what this stuff can do. Well, I’ll wait to read your article re what your findings were browser by browser for the transform-origin with percentage values issues.

      I’m definitely inspired to do a more full-blown animation sequence to kick off 2015 but I’ve gotta finish up my “simpler” tuts first hehe. So many interesting things to look into ex. your code has peeked my interest to do a double implementation in snap.svg|velocity|tweenmax just to see the differences. Thanks for the inspiration Petr and looking forward to see what else you come up with.

      Back on topic for the 3 ways to animate svg, I’d personally love to see more examples like Petr’s pulled off with SMIL but it just seems like it would be ridiculously impractical to try to do this sort of thing declaratively. Seems better for simpler animations. Plus the support issue makes it hard for me to get super motivated to perfect or dive deep in to SMIL – jm2c (maybe I’ll be convinced otherwise)

  5. User Avatar
    Rob Levin
    Permalink to comment#

    Hi Chris! Watched this and it was a nice intro comparison between CSS vs. SMIL vs. JS. I’ve read but yet to play with SMIL so it was particularly useful to see your SMIL tinkering here.

    You know me, the “gotcha guy” wink wink. There are two that come to mind:

    1. Should be mentioned that one of the compelling reasons to use JS and/or snap.svg lib vs CSS (maybe SMIL too???) is IE9+ will work with JS but not the others. This is a biggie for me personally (although I sure wish it was as easy and intuitive as CSS, huh!?)
    2. For my experiments, I’ve noticed that I seem to have much more success using snap.svg when I just go ahead and “make compound path” on all primitive shapes. I spent so long trying to animate polygon points, for example, and got frustrated and just went with a path and it work in minutes (I realize snap.svg supports animating polygon points, so probably “user error” of some kind, but it was very hard to find working examples that matched what I was trying to do).

    I mention 2., because, at first watching, it looked like you might have done the same (convert primitives to paths). Confirmed?

    Thanks for the vid tut Chris!

  6. User Avatar
    Luis
    Permalink to comment#

    Hi Chris, in your video you´re showing us an exported SVG robot with a cool markup semantics output, are you using an Illustrator script or some extension to make that?

    Nice tutorial as usual

    • User Avatar
      Chris Coyier
      Permalink to comment#

      I just snagged some robot vectors off of Shutterstock, I think. I keep a subscription there because I often need little bits of stock photos / vectors, but they aren’t a sponsor or anything, just a business expense.

  7. User Avatar
    Clare
    Permalink to comment#

    Hi,

    Is there any way that anyone can think of to make a simple eyelid opening and closing? Like the circle, only with angles at the sides. I couldn’t do a straight line. It’d have to be a curved line that changes from up to down. Any ideas? This may be too complicated to try, but I figured I’d try to figure it out.

  8. User Avatar
    Paeon
    Permalink to comment#

    Hi, I am told that google chrome will no longer support SMIL. It’s because no one was really using it. I would counter that until svg became a “thing” (within the last year) there was no reason to use SMIL. But now that svg IS a thing, now is not the time to drop support for it.

  9. User Avatar
    webafrooz
    Permalink to comment#


    <path id=”left-leg” ..
    in css:
    .left-leg must be #left-leg

    [ illegal selector!]

  10. User Avatar
    Iain

    Awesome intro. Thanks!

  11. User Avatar
    Gael
    Permalink to comment#

    Hi
    im a newbie and i want to add the code to my wordpress..how do i go about that in my dashboard?Thanks

Submit a Comment

Posting Code

You may write comments in Markdown. This makes code easy to post, as you can write inline code like `<div>this</div>` or multiline blocks of code in triple backtick fences (```) with double new lines before and after.

Code of Conduct

Absolutely anyone is welcome to submit a comment here. But not all comments will be posted. Think of it like writing a letter to the editor. All submitted comments will be read, but not all published. Published comments will be on-topic, helpful, and further the discussion or debate.

Want to tell us something privately?

Feel free to use our contact form. That's a great place to let us know about typos or anything off-topic.

icon-closeicon-emailicon-linkicon-logo-staricon-menuicon-searchicon-staricon-tag