SVG logo animation with Anime.js

Many a library exist for animation on the web these days. Anime is my favorite. It’s approachable 1, comes with decent documentation and examples, and packs phenomenal cosmic power in an itty bitty living space (9.15 KB minified).

Anime lets you run animations on just about anything — CSS, SVG, DOM attributes — but what I love most about it is its ability to run animations on individual CSS transform properties. With Anime, you can write something like this:

anime({
    // specfiy a string selector, a DOM node, or an array of those things
    targets: ".a-dead-body",
    // we can animate on a specific transform property
    translateX: 100,
    // as many of these as you want
    rotate: 180,
    // doesn't have to be a transform, anything numerically-based will do
    color: "rgb(255,0,0)",
    // how long?
    duration: 1000,
    // cool built-in easing curves
    easing: "easeOutElastic",
    // do something afterward
    complete: function() {
        console.log("It's alive!");
    }
});

Which could produce something like this:

💀 Click me!

See?

Of course, that’s a nearly useless animation. Let me show you a real-world example — the logo animation on the home page of this very website.

Animating my logo

My logo (or personal mark, as I like to call it) is an SVG that resembles the initials “bw” stylized to look like a futuristic “w”. Take a look below and, when you are ready, click the animate button to see how we want our final animation to look.

In order to achieve this effect, we need to break the logo into three distinct pieces:

  • #ex1-logo
    • #ex1-logo-left
    • #ex1-logo-middle
    • #ex1-logo-right

First we need to get our logo into its pre-animation state — the starting point. To do that, we need to do the following:

  1. Make sure the whole logo starts off transparent
  2. Explicitly make the middle piece transparent
  3. Bump the entire logo down by half of its height
  4. Swap the left and right pieces
$("#ex1-logo").css({
    "transform": "translateY(50%)",
    "opacity": 0
});
$("#ex1-logo-middle").css("opacity", 0);
$("#ex1-logo-left").css("transform", "translateX(100%)");
$("#ex1-logo-right").css("transform", "translateX(-100%)");

Good. If you could see it at this point, it would resemble an arrow pointing upward. Now we can bring in Anime.

First, we want to move the whole logo group back up to its original position while simultaneously fading it in.

anime({
    targets: "#ex1-logo",
    translateY: ["50%", "0%"], // start value, end value
    opacity: 1,
    easing: "easeOutSine",
}); 

Sweet. As soon as that animation finishes, we want to do two things simultaneously:

  • Move the left and right pieces back to their original positions
  • Fade in the middle piece

We can latch on to Anime’s complete callback in order to run code after the first animation finishes.

anime({
    targets: "#ex1-logo",
    translateY: ["50%", "0%"], // start value, end value
    opacity: 1,
    easing: "easeOutSine",
    complete: function() {
        // MORE ANIME HERE!
    }
}); 

I’m going to fire two different Anime animations inside that callback. First, the hard one — swapping the left and right pieces back to their original position.

anime({
    targets: "#ex1-logo",
    translateY: ["50%", "0%"], // start value, end value
    opacity: 1,
    easing: "easeOutSine",
    complete: function() {
        anime({
            targets: ["#ex1-logo-left", "#ex1-logo-right"],
            translateX: function(el, index) {
                if($(el).attr("id") === "ex1-logo-left") {
                    return ["100%", "0%"];
                } else {
                    return ["-100%", "0%"];
                }
            },
            easing: "easeInOutQuad",
            duration: 500
        });
    }
}); 

What?! We have two targets? And what is this translateX voodoo?

A very cool feature of Anime is its ability to accept a function instead of a static value. The function will run for each target specified, and automatically recieves the current DOM element (el) and the zero-based index of the current target. Very similar to a jQuery .each() function, if you are familiar with that.

For each target, we can now do some logic to return back the value we want. In our case, we want to give the left piece different start and end points from the right piece. That’s done with an if statement that checks the element’s id.

On to the next part. We’re just going to run another Anime animation at (roughly) the same time by calling it right after this one.

This is the final state of our animation code, so here it is all together now:

$("#ex1-logo").css({
    "transform": "translateY(50%)",
    "opacity": 0
});
$("#ex1-logo-middle").css("opacity", 0);
$("#ex1-logo-left").css("transform", "translateX(100%)");
$("#ex1-logo-right").css("transform", "translateX(-100%)");

anime({
    targets: "#ex1-logo",
    translateY: ["50%", "0%"], // start value, end value
    opacity: 1,
    easing: "easeOutSine",
    complete: function() {
        anime({
            targets: ["#ex1-logo-left", "#ex1-logo-right"],
            translateX: function(el, index) {
                if($(el).attr("id") === "ex1-logo-left") {
                    return ["100%", "0%"];
                } else {
                    return ["-100%", "0%"];
                }
            },
            easing: "easeInOutQuad",
            duration: 500
        });

        anime({
            targets: "#ex1-logo-middle",
            opacity: 1,
            easing: "easeInOutExpo",
        });
    }
}); 

Lastly, we fade in the middle piece, and we even use a fancy smancy easing curve to do it.

One more time, with feeling

The final animation looks something like this, as we saw earlier.

If that seemed like a lot, don’t worry — it kind of is. It might seem a bit odd at first, but it’s just a matter of thinking through what you want to do. Think about it in terms of a starting point and an ending point. Then write out in words what you want to accomplish. If you get stuck, the docs are your friend.

Want to see more tutorials? Get in touch and let me know what you would like to see.


  1. Learning any library is hard. But Anime’s properties correspond closely with CSS properties which helps I think.