Animated SVG Icons with Snap.svg

Using SVGs on websites is becoming more and more easy with great libraries like Snap.svg. Today we want to explore what we can do with it and animate some SVG icons as a practical example.

SVG has been one of the most underused technologies when it comes to web development. Despite it’s usefulness and powerful possibilities it’s still a mystery to many and when it comes to integrating it and using animations, many developers don’t know where to start. With great libraries like Snap.svg the use of SVG assets becomes more easy and today we’d like to explore how to animate SVG icons.

You’ve surely seen some great examples of animated icons using CSS transitions and animations like the Navicon Transformicons by Bennett Feely which were explained in this excellent collaborative tutorial by Sara Soueidan. We want to try to do something similar using SVG icons with the help of Snap.svg.

Please note that we are working with a modern JavaScript library for manipulating our SVGs. Older and non-supporting browsers will not be capable of all features.

The first thing we do is to create some SVG icons using an SVG editor like Inkscape. We used a size of 64×64 pixel for the icons.
Inkscape

For each icon we want a special animation to happen. For example, for the zoom icon we’ll want to scale up the plus path. We’ll define what will happen for each icon in our script.
We’ll add the icons dynamically to our page using Snap.svg and if SVG is not supported we’ll simply show a background image for the span elements that we use to wrap each graphic:

<section class="si-icons">
	<span class="si-icon si-icon-play" data-icon-name="play"></span>
	<span class="si-icon si-icon-monitor" data-icon-name="monitor"></span>
	<!-- ... -->
</section>

With the help of Modernizr we can define the fallback in our CSS:

.no-svg .si-icon-play { background-image: url('../png/play.png') }

.no-svg .si-icon-monitor { background-image: url('../png/monitor.png') }

The PNG icons were generated with the fabulous iconizr tool. You could as well use CSS sprites and define a active class but we decided to just do a very simple fallback for this demo.

If you add the class “si-icon-reverse” to the span, the icon will be initially rendered with the reversed “shape”. For instance, if you want to display the stop icon rather than the play icon, you can achieve that in the following way:

	<span class="si-icon si-icon-reverse" data-icon-name="play"></span>

Tiny break: 📬 Want to stay up to date with frontend and trends in web design? Subscribe and get our Collective newsletter twice a tweek.

Now let’s take a look at what we are doing in the JavaScript. The idea is to do something with each icon. That can be some kind of transformation, like a rotation or a scale, or a change of a path altogether. With the Snap.svg we can dynamically load our SVGs, which we store in a folder, and manipulate them in a very practical way thanks to the powerful API.

The configuration variable for the icons, “svgIconConfig” has all the animation settings for each icon.
Let’s take a look at its logic:

[icon name - same name given to the data-icon-name] : { 
	url : [url of the svg file],
	animation : [array of animation configs for each element]
}

Each animation config can have the following structure:

{ 
	el : [element selector], 
	animProperties : [animation config for the initial/from and final/to state]
}

Let’s take a look at some possible values for the initial and final state:

from : { 
	val : [animation name value pairs],
	after : [attribute name value pairs to be applied when the animation ends],
	before : [attribute name value pairs to be applied before the animation starts],
	delayFactor : [animation delay factor],
	animAfter : [animation name value pairs to be applied when the animation ends] 
}

A real example would be the following:

myIconName : { 
	url : 'svgs/myIconName.svg',
	animation : [
		{ 
			el : 'path:nth-child(5)', 
			animProperties : { 
				from : { val : '{"path" : "m 61.693118,24.434001 -59.386236,0 29.692524,19.897984 z"}', animAfter : '{"stroke" : "#000"}' }, 
				to : { val : '{"path" : "m 61.693118,24.434001 -59.386236,0 29.692524,-19.7269617 z"}', animAfter : '{"stroke" : "#444"}' }
				} 
		},
		{ 
			el : 'rect:nth-child(3)', 
			animProperties : { 
				from : { val : '{"transform" : "t0 0"}', after : '{ "opacity" : 0 }' }, 
				to : { val : '{"transform" : "t-10 -10"}', before : '{ "opacity" : 1 }' }
			} 
		}
]
}

You can initialize the icons like this:

new svgIcon( element, configuration [, options] );

And these are the possible options:

{
    speed : 200, // animation speed
    easing : mina.linear, // animation esing
    evtoggle : 'click', // event: click || mouseover
    size : { w : 64, h : 64 }, // size
    onLoad : function() { return false; }, // callback on svg load
    onToggle : function() { return false; } // callback on toggle (click or mouseover/mouseout)
}

Check out the demo to see some examples for different sizes, easings and activations (on click and on hover).

We hope you enjoy this experiment and find it useful!

Manoela Ilic

Manoela is the main tinkerer at Codrops. With a background in coding and passion for all things design, she creates web experiments and keeps frontend professionals informed about the latest trends.

Stay in the loop: Get your dose of frontend twice a week

Fresh news, inspo, code demos, and UI animations—zero fluff, all quality. Make your Mondays and Thursdays creative!

Feedback 95

Comments are closed.
  1. Great work… but it doesn’t work for me… at least the demo that I have downloaded :S….

    it seems like something is wrong: /svg/equalizer.svg. Received an invalid response. Origin ‘null’ is therefore not allowed access.

    Thanks

    • The svg must be in the same server as the main index file. Which is easy to do if you use xampp.

  2. Should it work on the latest Chrome?

    I have Version 37.0.2062.120 : No icons show.

    Works on Firefox though. Why not Chrome?

  3. Brilliant and inspiring. This be the future of the cyberwebs. Sorry for the noob questions but:
    1. I get the basic motions but how are shape morphs happening? For examples, the smiley mouth or play > stop button.
    2.I know some of this is doable via Ill > EDGE animate but there’s still plenty of coding involved. Are there apps that make building these easier for non-coders?

  4. Hi! Good work! Thanx! How can I control of trigger event or animation of icon? For example, I want to use hamburger for hide/show menu. After clicking on the menu item icon svg should change its state. How can I do it?

    • You could use jQuery to trigger an event on click and possibly toggleClass for for a menu to appear and hide.

  5. I got the error, that :

    “TypeError: this.el is null” in svgicons.min.js in line 59.
    ( …h),this.svg.attr(“viewBox”,”0 0 64 64″),this.el.appendChild(this.svg.node),this…. )

    I includet snap library, svgicons-config.js, svgicons.js, the needed css and the container with the html elements. And i just copied the JS for initialization from the example …

    I dont get it 🙁 😀

  6. Uhh! I got it! I optimized my images by using svgo extension for imagemin cintrib in grunt – and the optimized images do not work, while the originals do. Hmm. Is there a way for optimizing without crashing?

  7. I am triggering a click on the icons by a click on another element. Thats working fine – the icon animation runs. But, when the trigger is fired by a mobile user it is not working!

    $(‘h1 p’).on(‘click tap’,function(e){
    $(this).next(‘.si-icon’).trigger(‘click’);
    });

    Any solution for the problem?

    • Hi Martin,

      I am triggering a click on the icons by a click on another element.

      How do you do that??

      Thanks

  8. Great tutorial Mary! I know this was touched on a few comments ago – but I’m trying to incorporate the hamburgerCross example in my WordPress site. It’s failing to show up and animate due to this error "Uncaught TypeError: Cannot read property 'appendChild' of null". I’ve read on stackoverflow that its probably because the script is loading before it appears in the DOM. However, shouldn’t appendChild add the element to the DOM?. How can I correct this? I added some basic jquery to toggleClass, but that doesn’t seem to work. btw – this is for the responsive menu hamburger icon. Thanks in advance for any feedback you can provide. 🙂

  9. Hello, is it possible to use the plusCross to open a div within it? i am having one underneath it, but i want as soon as i press it a div opens in the plusCross. so that when you open it it will expand and the cross will be in the corner of that div. i cant get it to work. i need some help here.

  10. Hey,

    I absolutely love these animated icons. So i created a svg with an animation for my mobile navigation. So when i click on the icon, my mobile navigation opens and the icon animates. If i click on the svg, the navigation closes and the icon animates again. But when i click on a link in the navigation, my icon does not animate. I tried many things with a toggle or a trigger, but finally i do not make it runnig.

    Can anyone explain me, how i fix it?

    Thanks and best regards
    Joko

  11. Hi, what if i want a multi state animation? For example want to have a shape A morphing into B and afterwards into C? Thanks for answer.

  12. Hi,

    can i roll back the effect when clicking on another object??
    Meaning, if i click on a “menu” icon and then cliking on another icon, the first one rollback alone.
    Can i do this?? thanks

  13. Hello!
    Help me, please.
    I can’t open the example. I downloaded a source, and when i open index.html i see