Creating Animated Icons with SVGs

We will be recreating the dark mode toggle I used for this site.

Prerequisite

Working knowledge of React, Javascript, Helps to know about SVG's

What we will be building

Preparing our Icon of choice

We will be using the Moon svg provided by the great folks at Radix UI.

<svg
	width="15"
	height="15"
	viewBox="0 0 15 15"
	fill="none"
	xmlns="http://www.w3.org/2000/svg"
>
	<path
		d="M2.89998 0.499976C2.89998 0.279062 2.72089 0.0999756 2.49998 0.0999756C2.27906 0.0999756 2.09998 0.279062 2.09998 0.499976V1.09998H1.49998C1.27906 1.09998 1.09998 1.27906 1.09998 1.49998C1.09998 1.72089 1.27906 1.89998 1.49998 1.89998H2.09998V2.49998C2.09998 2.72089 2.27906 2.89998 2.49998 2.89998C2.72089 2.89998 2.89998 2.72089 2.89998 2.49998V1.89998H3.49998C3.72089 1.89998 3.89998 1.72089 3.89998 1.49998C3.89998 1.27906 3.72089 1.09998 3.49998 1.09998H2.89998V0.499976ZM5.89998 3.49998C5.89998 3.27906 5.72089 3.09998 5.49998 3.09998C5.27906 3.09998 5.09998 3.27906 5.09998 3.49998V4.09998H4.49998C4.27906 4.09998 4.09998 4.27906 4.09998 4.49998C4.09998 4.72089 4.27906 4.89998 4.49998 4.89998H5.09998V5.49998C5.09998 5.72089 5.27906 5.89998 5.49998 5.89998C5.72089 5.89998 5.89998 5.72089 5.89998 5.49998V4.89998H6.49998C6.72089 4.89998 6.89998 4.72089 6.89998 4.49998C6.89998 4.27906 6.72089 4.09998 6.49998 4.09998H5.89998V3.49998ZM1.89998 6.49998C1.89998 6.27906 1.72089 6.09998 1.49998 6.09998C1.27906 6.09998 1.09998 6.27906 1.09998 6.49998V7.09998H0.499976C0.279062 7.09998 0.0999756 7.27906 0.0999756 7.49998C0.0999756 7.72089 0.279062 7.89998 0.499976 7.89998H1.09998V8.49998C1.09998 8.72089 1.27906 8.89997 1.49998 8.89997C1.72089 8.89997 1.89998 8.72089 1.89998 8.49998V7.89998H2.49998C2.72089 7.89998 2.89998 7.72089 2.89998 7.49998C2.89998 7.27906 2.72089 7.09998 2.49998 7.09998H1.89998V6.49998ZM8.54406 0.98184L8.24618 0.941586C8.03275 0.917676 7.90692 1.1655 8.02936 1.34194C8.17013 1.54479 8.29981 1.75592 8.41754 1.97445C8.91878 2.90485 9.20322 3.96932 9.20322 5.10022C9.20322 8.37201 6.82247 11.0878 3.69887 11.6097C3.45736 11.65 3.20988 11.6772 2.96008 11.6906C2.74563 11.702 2.62729 11.9535 2.77721 12.1072C2.84551 12.1773 2.91535 12.2458 2.98667 12.3128L3.05883 12.3795L3.31883 12.6045L3.50684 12.7532L3.62796 12.8433L3.81491 12.9742L3.99079 13.089C4.11175 13.1651 4.23536 13.2375 4.36157 13.3059L4.62496 13.4412L4.88553 13.5607L5.18837 13.6828L5.43169 13.7686C5.56564 13.8128 5.70149 13.8529 5.83857 13.8885C5.94262 13.9155 6.04767 13.9401 6.15405 13.9622C6.27993 13.9883 6.40713 14.0109 6.53544 14.0298L6.85241 14.0685L7.11934 14.0892C7.24637 14.0965 7.37436 14.1002 7.50322 14.1002C11.1483 14.1002 14.1032 11.1453 14.1032 7.50023C14.1032 7.25044 14.0893 7.00389 14.0623 6.76131L14.0255 6.48407C13.991 6.26083 13.9453 6.04129 13.8891 5.82642C13.8213 5.56709 13.7382 5.31398 13.6409 5.06881L13.5279 4.80132L13.4507 4.63542L13.3766 4.48666C13.2178 4.17773 13.0353 3.88295 12.8312 3.60423L12.6782 3.40352L12.4793 3.16432L12.3157 2.98361L12.1961 2.85951L12.0355 2.70246L11.8134 2.50184L11.4925 2.24191L11.2483 2.06498L10.9562 1.87446L10.6346 1.68894L10.3073 1.52378L10.1938 1.47176L9.95488 1.3706L9.67791 1.2669L9.42566 1.1846L9.10075 1.09489L8.83599 1.03486L8.54406 0.98184ZM10.4032 5.30023C10.4032 4.27588 10.2002 3.29829 9.83244 2.40604C11.7623 3.28995 13.1032 5.23862 13.1032 7.50023C13.1032 10.593 10.596 13.1002 7.50322 13.1002C6.63646 13.1002 5.81597 12.9036 5.08355 12.5522C6.5419 12.0941 7.81081 11.2082 8.74322 10.0416C8.87963 10.2284 9.10028 10.3497 9.34928 10.3497C9.76349 10.3497 10.0993 10.0139 10.0993 9.59971C10.0993 9.24256 9.84965 8.94373 9.51535 8.86816C9.57741 8.75165 9.63653 8.63334 9.6926 8.51332C9.88358 8.63163 10.1088 8.69993 10.35 8.69993C11.0403 8.69993 11.6 8.14028 11.6 7.44993C11.6 6.75976 11.0406 6.20024 10.3505 6.19993C10.3853 5.90487 10.4032 5.60464 10.4032 5.30023Z"
		fill="currentColor"
		fill-rule="evenodd"
		clip-rule="evenodd"
	></path>
</svg>

Requirements ✅

Any icon can be animated, we could simple apply a scale and rotation on mounting or clicking. But for what I wanted I had some specific requirements.

I chose this icon specifically because it has 3 stars. When I started looking around for an icon, I had already decided I wanted something with stars in it, stars that I could animate. In other words I had an idea of the animation that I wanted. This is not always the case. For example, the sun icon was just me scouring through icon sets until I found something that matched the moon icon’s style and could be animated.

Path Element

A path is an svg element that can be used to create any complex shape i.e. arcs, curves, stars.

A great place to learn more about SVG paths is Nanda’s guide on interactive SVG paths.


Anatomy of an SVG 📈

Some eagle eyed observers would have noticed that one of our requirements is that the svg needs separate paths for each element. Even more eagle like observers would have noticed the svg code I shared above only has one path despite the multiple elements. So what is happening here? Well to put it simply, think of paths like a printer head: We can move the printer head around the page, place it down to start drawing, draw a shape, lift it back up, move to another part of the page, put it down, and draw another shape. Path’s work similarly where we can draw a line or curve and then move to another part and draw another line or curve all within a single path. An example of these commands is this:

M 3 3
L 3 7
L 7 7
L 7 3
Z

Imagining the SVG canvas as a printer. If we need to start our print somewhere in the middle of the page, we need to move the print head to that position first. This is what M 3 3 does. It moves the starting point of our line to (3, 3) which is 3 on the x axis and 3 on the y axis.

  1. M 3 3 is the start of our line.
  2. L 3 7 L means draw a line to (3, 7). If we used M it would just move the ‘head’ to the coordinates without drawing a line.
  3. L 7 7 similarly draws a line from the previous position to the new coordinates.
  4. L 7 3 makes another line to (7, 3).
  5. Z ends the path, making a line straight to the beginning (0, 0).

If we want to make an SVG with this path it would be the following:

<svg
	height="200"
	width="200"
	viewBox="0 0 10 10"
	xmlns="http://www.w3.org/2000/svg"
>
	<path
		d="M3 3L3 7L7 7L 7 3Z"
		stroke-width="0.2"
		stroke="purple"
		fill="none"
	></path>
</svg>

This renders to:

The Viewbox

Considering we are using coordinates what are the limits of our graph? Well this is where the viewBox comes into play. viewBox="0 0 10 10" translates to: (starting x coordinate, starting y coordinate, height, width). starting x coordinates means instead of 0 start at 4.

Awesome! We now have a rudimentary understanding of SVG’s with a single path. What about svg’s with multiple paths? Well lets look at the moon svg from the beginning again:

<svg>
	<path d="M2.89998 0.499976C2.89998 0.279062 2.72089 0.0999756 2.49998 0.0999756C2.27906 0.0999756 2.09998 0.279062 2.09998 0.499976V1.09998H1.49998C1.27906 1.09998 1.09998 1.27906 1.09998 1.49998C1.09998 1.72089 1.27906 1.89998 1.49998 1.89998H2.09998V2.49998C2.09998 2.72089 2.27906 2.89998 2.49998 2.89998C2.72089 2.89998 2.89998 2.72089 2.89998 2.49998V1.89998H3.49998C3.72089 1.89998 3.89998 1.72089 3.89998 1.49998C3.89998 1.27906 3.72089 1.09998 3.49998 1.09998H2.89998V0.499976ZM5.89998 3.49998C5.89998 3.27906 5.72089 3.09998 5.49998 3.09998C5.27906 3.09998 5.09998 3.27906 5.09998 3.49998V4.09998H4.49998C4.27906 4.09998 4.09998 4.27906 4.09998 4.49998C4.09998 4.72089 4.27906 4.89998 4.49998 4.89998H5.09998V5.49998C5.09998 5.72089 5.27906 5.89998 5.49998 5.89998C5.72089 5.89998 5.89998 5.72089 5.89998 5.49998V4.89998H6.49998C6.72089 4.89998 6.89998 4.72089 6.89998 4.49998C6.89998 4.27906 6.72089 4.09998 6.49998 4.09998H5.89998V3.49998ZM1.89998 6.49998C1.89998 6.27906 1.72089 6.09998 1.49998 6.09998C1.27906 6.09998 1.09998 6.27906 1.09998 6.49998V7.09998H0.499976C0.279062 7.09998 0.0999756 7.27906 0.0999756 7.49998C0.0999756 7.72089 0.279062 7.89998 0.499976 7.89998H1.09998V8.49998C1.09998 8.72089 1.27906 8.89997 1.49998 8.89997C1.72089 8.89997 1.89998 8.72089 1.89998 8.49998V7.89998H2.49998C2.72089 7.89998 2.89998 7.72089 2.89998 7.49998C2.89998 7.27906 2.72089 7.09998 2.49998 7.09998H1.89998V6.49998ZM8.54406 0.98184L8.24618 0.941586C8.03275 0.917676 7.90692 1.1655 8.02936 1.34194C8.17013 1.54479 8.29981 1.75592 8.41754 1.97445C8.91878 2.90485 9.20322 3.96932 9.20322 5.10022C9.20322 8.37201 6.82247 11.0878 3.69887 11.6097C3.45736 11.65 3.20988 11.6772 2.96008 11.6906C2.74563 11.702 2.62729 11.9535 2.77721 12.1072C2.84551 12.1773 2.91535 12.2458 2.98667 12.3128L3.05883 12.3795L3.31883 12.6045L3.50684 12.7532L3.62796 12.8433L3.81491 12.9742L3.99079 13.089C4.11175 13.1651 4.23536 13.2375 4.36157 13.3059L4.62496 13.4412L4.88553 13.5607L5.18837 13.6828L5.43169 13.7686C5.56564 13.8128 5.70149 13.8529 5.83857 13.8885C5.94262 13.9155 6.04767 13.9401 6.15405 13.9622C6.27993 13.9883 6.40713 14.0109 6.53544 14.0298L6.85241 14.0685L7.11934 14.0892C7.24637 14.0965 7.37436 14.1002 7.50322 14.1002C11.1483 14.1002 14.1032 11.1453 14.1032 7.50023C14.1032 7.25044 14.0893 7.00389 14.0623 6.76131L14.0255 6.48407C13.991 6.26083 13.9453 6.04129 13.8891 5.82642C13.8213 5.56709 13.7382 5.31398 13.6409 5.06881L13.5279 4.80132L13.4507 4.63542L13.3766 4.48666C13.2178 4.17773 13.0353 3.88295 12.8312 3.60423L12.6782 3.40352L12.4793 3.16432L12.3157 2.98361L12.1961 2.85951L12.0355 2.70246L11.8134 2.50184L11.4925 2.24191L11.2483 2.06498L10.9562 1.87446L10.6346 1.68894L10.3073 1.52378L10.1938 1.47176L9.95488 1.3706L9.67791 1.2669L9.42566 1.1846L9.10075 1.09489L8.83599 1.03486L8.54406 0.98184ZM10.4032 5.30023C10.4032 4.27588 10.2002 3.29829 9.83244 2.40604C11.7623 3.28995 13.1032 5.23862 13.1032 7.50023C13.1032 10.593 10.596 13.1002 7.50322 13.1002C6.63646 13.1002 5.81597 12.9036 5.08355 12.5522C6.5419 12.0941 7.81081 11.2082 8.74322 10.0416C8.87963 10.2284 9.10028 10.3497 9.34928 10.3497C9.76349 10.3497 10.0993 10.0139 10.0993 9.59971C10.0993 9.24256 9.84965 8.94373 9.51535 8.86816C9.57741 8.75165 9.63653 8.63334 9.6926 8.51332C9.88358 8.63163 10.1088 8.69993 10.35 8.69993C11.0403 8.69993 11.6 8.14028 11.6 7.44993C11.6 6.75976 11.0406 6.20024 10.3505 6.19993C10.3853 5.90487 10.4032 5.60464 10.4032 5.30023Z"></path>
</svg>

I have simplified the SVG for reference.

You will notice that there is only one path for the 4 elements in our svg. Normally this is great, save space by having it all in one path. But for our needs this is not the best. If we want to animate each element separately we cannot.

The solution? We separate our single path into 4 smaller paths. I imagine this can be done in a tool like Adobe Illustrator, but I wanted a really easy way to do this. After many attempts that ended in spectacular failure I found this. We are now able to easily split up our svg into multiple paths. Using this I have built a small tool that allows you to easily split your svg’s path. It is called The Magical SVG Path Splitter Tool 8000. Just paste your svg in, and it will spit it out a svg with separated paths.

Awesome! We now have everything we need to begin animating!


Animating our Icon 🎬

We will be using React and Framer Motion to animate our icon.

Ok lets see what our moon icon looks like now:

<svg width="80" height="80" viewBox="0 0 15 15" fill="none" color="red" xmlns="http://www.w3.org/2000/svg" id="original">
	<path d="M 2.9 0.5 C 2.9 0.279 2.721 0.1 2.5 0.1 C 2.279 0.1 2.1 0.279 2.1 0.5 L 2.1 1.1 L 1.5 1.1 C 1.279 1.1 1.1 1.279 1.1 1.5 C 1.1 1.721 1.279 1.9 1.5 1.9 L 2.1 1.9 L 2.1 2.5 C 2.1 2.721 2.279 2.9 2.5 2.9 C 2.721 2.9 2.9 2.721 2.9 2.5 L 2.9 1.9 L 3.5 1.9 C 3.721 1.9 3.9 1.721 3.9 1.5 C 3.9 1.279 3.721 1.1 3.5 1.1 L 2.9 1.1 L 2.9 0.5 Z" fill="currentColor"></path>
	<path d="M 5.9 3.5 C 5.9 3.279 5.721 3.1 5.5 3.1 C 5.279 3.1 5.1 3.279 5.1 3.5 L 5.1 4.1 L 4.5 4.1 C 4.279 4.1 4.1 4.279 4.1 4.5 C 4.1 4.721 4.279 4.9 4.5 4.9 L 5.1 4.9 L 5.1 5.5 C 5.1 5.721 5.279 5.9 5.5 5.9 C 5.721 5.9 5.9 5.721 5.9 5.5 L 5.9 4.9 L 6.5 4.9 C 6.721 4.9 6.9 4.721 6.9 4.5 C 6.9 4.279 6.721 4.1 6.5 4.1 L 5.9 4.1 L 5.9 3.5 Z" fill="currentColor"></path>
	<path d="M 1.9 6.5 C 1.9 6.279 1.721 6.1 1.5 6.1 C 1.279 6.1 1.1 6.279 1.1 6.5 L 1.1 7.1 L 0.5 7.1 C 0.279 7.1 0.1 7.279 0.1 7.5 C 0.1 7.721 0.279 7.9 0.5 7.9 L 1.1 7.9 L 1.1 8.5 C 1.1 8.721 1.279 8.9 1.5 8.9 C 1.721 8.9 1.9 8.721 1.9 8.5 L 1.9 7.9 L 2.5 7.9 C 2.721 7.9 2.9 7.721 2.9 7.5 C 2.9 7.279 2.721 7.1 2.5 7.1 L 1.9 7.1 L 1.9 6.5 Z" fill="currentColor"></path>
	<path d="M 8.544 0.982 L 8.246 0.942 C 8.033 0.918 7.907 1.166 8.029 1.342 C 8.17 1.545 8.3 1.756 8.418 1.974 C 8.919 2.905 9.203 3.969 9.203 5.1 C 9.203 8.372 6.822 11.088 3.699 11.61 C 3.457 11.65 3.21 11.677 2.96 11.691 C 2.746 11.702 2.627 11.954 2.777 12.107 C 2.846 12.177 2.915 12.246 2.987 12.313 L 3.059 12.38 L 3.319 12.605 L 3.507 12.753 L 3.628 12.843 L 3.815 12.974 L 3.991 13.089 C 4.112 13.165 4.235 13.238 4.362 13.306 L 4.625 13.441 L 4.886 13.561 L 5.188 13.683 L 5.432 13.769 C 5.566 13.813 5.701 13.853 5.839 13.889 C 5.943 13.916 6.048 13.94 6.154 13.962 C 6.28 13.988 6.407 14.011 6.535 14.03 L 6.852 14.069 L 7.119 14.089 C 7.246 14.097 7.374 14.1 7.503 14.1 C 11.148 14.1 14.103 11.145 14.103 7.5 C 14.103 7.25 14.089 7.004 14.062 6.761 L 14.026 6.484 C 13.991 6.261 13.945 6.041 13.889 5.826 C 13.821 5.567 13.738 5.314 13.641 5.069 L 13.528 4.801 L 13.451 4.635 L 13.377 4.487 C 13.218 4.178 13.035 3.883 12.831 3.604 L 12.678 3.404 L 12.479 3.164 L 12.316 2.984 L 12.196 2.86 L 12.036 2.702 L 11.813 2.502 L 11.493 2.242 L 11.248 2.065 L 10.956 1.874 L 10.635 1.689 L 10.307 1.524 L 10.194 1.472 L 9.955 1.371 L 9.678 1.267 L 9.426 1.185 L 9.101 1.095 L 8.836 1.035 L 8.544 0.982 Z" fill="currentColor"></path>
	<path d="M 10.403 5.3 C 10.403 4.276 10.2 3.298 9.832 2.406 C 11.762 3.29 13.103 5.239 13.103 7.5 C 13.103 10.593 10.596 13.1 7.503 13.1 C 6.636 13.1 5.816 12.904 5.084 12.552 C 6.542 12.094 7.811 11.208 8.743 10.042 C 8.88 10.228 9.1 10.35 9.349 10.35 C 9.763 10.35 10.099 10.014 10.099 9.6 C 10.099 9.243 9.85 8.944 9.515 8.868 C 9.577 8.752 9.637 8.633 9.693 8.513 C 9.884 8.632 10.109 8.7 10.35 8.7 C 11.04 8.7 11.6 8.14 11.6 7.45 C 11.6 6.76 11.041 6.2 10.351 6.2 C 10.385 5.905 10.403 5.605 10.403 5.3 Z" fill="currentColor"></path>
</svg>

Perfect! All our path’s are now separated. We could now just use CSS to animate each path. for example:

I just added some simple wiggling actions here. Have a look:

#path1 {
wiggle 1s ease-in-out infinite
}

#path2 {
wiggle 1s ease-in-out infinite
}

#path3 {
inverse-wiggle 1s ease-in-out infinite
}

#path4 {
inverse-wiggle 1s ease-in-out infinite
}

@keyframes wiggle {
'0%, 100%': { transform: 'rotate(-6deg)' },
'50%': { transform: 'rotate(6deg)' },
}

@keyframes inverse-wiggle {
'0%, 100%': { transform: 'rotate(6deg)' },
'50%': { transform: 'rotate(-6deg)' },
}

Ok this is great, but what I really want are some spring based animations for my icons. I want the stars to appear one by one.

We could achieve spring based animations with CSS using some custom cubic bezier timings. I would do this if I want to avoid Javascript or it is a one off. However for this article I want to focus on how I would do this with React. My go to animation library with React is Framer Motion. So how could be animate this icon in framer motion?


Actually animating our Icons 🎬 🤔

This is not meant to be a framer motion tutorial. Check out the docs here.

Framer motion makes it ridiculously easy to add animations to our apps. Here is a sample div with the necessary framer motion syntax.

<motion.div
	initial={{ opacity: 0, scale: 0.5 }}
	animate={{ opacity: 1, scale: 1 }}
	transition={{ duration: 0.5 }}
>
	Hi There 👋
</motion.div>

Lets break down what is happening:

  1. Instead of a div we are using a motion.div
  2. initial is the initial state of our div
  3. animate is the final state of our animation
  4. transition is how our animation transitions from the initial state to the animate state. This could involve whether it is a linear animation or a spring animation, its duration, or its delay, etc

One thing to know is that framer motion exports a motion component for every html and svg element. Yes, they also export a motion.path element. So taking what we did with our motion.div lets add some simple animations to each path of our moon icon.

<svg
	height="100%"
	viewBox="0 0 15 15"
	fill="none"
	color="purple"
	xmlns="http://www.w3.org/2000/svg"
	id="original"
>
	<motion.path
		d="M 2.9 0.5 C 2.9 0.279 2.721 0.1 2.5 0.1 C 2.279 0.1 2.1 0.279 2.1 0.5 L 2.1 1.1 L 1.5 1.1 C 1.279 1.1 1.1 1.279 1.1 1.5 C 1.1 1.721 1.279 1.9 1.5 1.9 L 2.1 1.9 L 2.1 2.5 C 2.1 2.721 2.279 2.9 2.5 2.9 C 2.721 2.9 2.9 2.721 2.9 2.5 L 2.9 1.9 L 3.5 1.9 C 3.721 1.9 3.9 1.721 3.9 1.5 C 3.9 1.279 3.721 1.1 3.5 1.1 L 2.9 1.1 L 2.9 0.5 Z"
		fill="currentColor"
		initial={{ scale: 0.7, opacity: 0 }}
		animate={{ scale: 1, opacity: 1 }}
	/>
	<motion.path
		d="M 5.9 3.5 C 5.9 3.279 5.721 3.1 5.5 3.1 C 5.279 3.1 5.1 3.279 5.1 3.5 L 5.1 4.1 L 4.5 4.1 C 4.279 4.1 4.1 4.279 4.1 4.5 C 4.1 4.721 4.279 4.9 4.5 4.9 L 5.1 4.9 L 5.1 5.5 C 5.1 5.721 5.279 5.9 5.5 5.9 C 5.721 5.9 5.9 5.721 5.9 5.5 L 5.9 4.9 L 6.5 4.9 C 6.721 4.9 6.9 4.721 6.9 4.5 C 6.9 4.279 6.721 4.1 6.5 4.1 L 5.9 4.1 L 5.9 3.5 Z"
		fill="currentColor"
		initial={{ scale: 0.7, opacity: 0 }}
		animate={{ scale: 1, opacity: 1 }}
	/>
	<motion.path
		d="M 1.9 6.5 C 1.9 6.279 1.721 6.1 1.5 6.1 C 1.279 6.1 1.1 6.279 1.1 6.5 L 1.1 7.1 L 0.5 7.1 C 0.279 7.1 0.1 7.279 0.1 7.5 C 0.1 7.721 0.279 7.9 0.5 7.9 L 1.1 7.9 L 1.1 8.5 C 1.1 8.721 1.279 8.9 1.5 8.9 C 1.721 8.9 1.9 8.721 1.9 8.5 L 1.9 7.9 L 2.5 7.9 C 2.721 7.9 2.9 7.721 2.9 7.5 C 2.9 7.279 2.721 7.1 2.5 7.1 L 1.9 7.1 L 1.9 6.5 Z"
		fill="currentColor"
		initial={{ scale: 0.7, opacity: 0 }}
		animate={{ scale: 1, opacity: 1 }}
	/>
	<motion.path
		d="M 8.544 0.982 L 8.246 0.942 C 8.033 0.918 7.907 1.166 8.029 1.342 C 8.17 1.545 8.3 1.756 8.418 1.974 C 8.919 2.905 9.203 3.969 9.203 5.1 C 9.203 8.372 6.822 11.088 3.699 11.61 C 3.457 11.65 3.21 11.677 2.96 11.691 C 2.746 11.702 2.627 11.954 2.777 12.107 C 2.846 12.177 2.915 12.246 2.987 12.313 L 3.059 12.38 L 3.319 12.605 L 3.507 12.753 L 3.628 12.843 L 3.815 12.974 L 3.991 13.089 C 4.112 13.165 4.235 13.238 4.362 13.306 L 4.625 13.441 L 4.886 13.561 L 5.188 13.683 L 5.432 13.769 C 5.566 13.813 5.701 13.853 5.839 13.889 C 5.943 13.916 6.048 13.94 6.154 13.962 C 6.28 13.988 6.407 14.011 6.535 14.03 L 6.852 14.069 L 7.119 14.089 C 7.246 14.097 7.374 14.1 7.503 14.1 C 11.148 14.1 14.103 11.145 14.103 7.5 C 14.103 7.25 14.089 7.004 14.062 6.761 L 14.026 6.484 C 13.991 6.261 13.945 6.041 13.889 5.826 C 13.821 5.567 13.738 5.314 13.641 5.069 L 13.528 4.801 L 13.451 4.635 L 13.377 4.487 C 13.218 4.178 13.035 3.883 12.831 3.604 L 12.678 3.404 L 12.479 3.164 L 12.316 2.984 L 12.196 2.86 L 12.036 2.702 L 11.813 2.502 L 11.493 2.242 L 11.248 2.065 L 10.956 1.874 L 10.635 1.689 L 10.307 1.524 L 10.194 1.472 L 9.955 1.371 L 9.678 1.267 L 9.426 1.185 L 9.101 1.095 L 8.836 1.035 L 8.544 0.982 Z"
		fill="currentColor"
		initial={{ scale: 0.7, opacity: 0 }}
		animate={{ scale: 1, opacity: 1 }}
	/>
	<motion.path
		d="M 10.403 5.3 C 10.403 4.276 10.2 3.298 9.832 2.406 C 11.762 3.29 13.103 5.239 13.103 7.5 C 13.103 10.593 10.596 13.1 7.503 13.1 C 6.636 13.1 5.816 12.904 5.084 12.552 C 6.542 12.094 7.811 11.208 8.743 10.042 C 8.88 10.228 9.1 10.35 9.349 10.35 C 9.763 10.35 10.099 10.014 10.099 9.6 C 10.099 9.243 9.85 8.944 9.515 8.868 C 9.577 8.752 9.637 8.633 9.693 8.513 C 9.884 8.632 10.109 8.7 10.35 8.7 C 11.04 8.7 11.6 8.14 11.6 7.45 C 11.6 6.76 11.041 6.2 10.351 6.2 C 10.385 5.905 10.403 5.605 10.403 5.3 Z"
		fill="none"
		initial={{ scale: 0.7, opacity: 0 }}
		animate={{ scale: 1, opacity: 1 }}
	/>
</svg>

Now we have this:

We are getting there. A few things to notice so far:

  1. They all animate at the same time.
  2. They animate linearly i.e. no springiness. (I like my springiness)
  3. The opacity fading in does not look that great.

So let us fix these issues. Here is the same icon with the new changes:

<svg
	key={key}
	height="100%"
	viewBox="0 0 15 15"
	fill="none"
	xmlns="http://www.w3.org/2000/svg"
>
	<motion.path
		d="M 2.9 0.5 C 2.9 0.279 2.721 0.1 2.5 0.1 C 2.279 0.1 2.1 0.279 2.1 0.5 L 2.1 1.1 L 1.5 1.1 C 1.279 1.1 1.1 1.279 1.1 1.5 C 1.1 1.721 1.279 1.9 1.5 1.9 L 2.1 1.9 L 2.1 2.5 C 2.1 2.721 2.279 2.9 2.5 2.9 C 2.721 2.9 2.9 2.721 2.9 2.5 L 2.9 1.9 L 3.5 1.9 C 3.721 1.9 3.9 1.721 3.9 1.5 C 3.9 1.279 3.721 1.1 3.5 1.1 L 2.9 1.1 L 2.9 0.5 Z"
		fill="currentColor"
		initial={{ scale: 0.5, opacity: 0 }}
		animate={{ scale: 1, opacity: 1 }}
		transition={{ duration: 1, delay: 0.2, type: 'spring' }}
	/>
	<motion.path
		d="M 5.9 3.5 C 5.9 3.279 5.721 3.1 5.5 3.1 C 5.279 3.1 5.1 3.279 5.1 3.5 L 5.1 4.1 L 4.5 4.1 C 4.279 4.1 4.1 4.279 4.1 4.5 C 4.1 4.721 4.279 4.9 4.5 4.9 L 5.1 4.9 L 5.1 5.5 C 5.1 5.721 5.279 5.9 5.5 5.9 C 5.721 5.9 5.9 5.721 5.9 5.5 L 5.9 4.9 L 6.5 4.9 C 6.721 4.9 6.9 4.721 6.9 4.5 C 6.9 4.279 6.721 4.1 6.5 4.1 L 5.9 4.1 L 5.9 3.5 Z"
		fill="currentColor"
		initial={{ scale: 0.5, opacity: 0 }}
		animate={{ scale: 1, opacity: 1 }}
		transition={{ duration: 1, delay: 0.4, type: 'spring' }}
	/>
	<motion.path
		d="M 1.9 6.5 C 1.9 6.279 1.721 6.1 1.5 6.1 C 1.279 6.1 1.1 6.279 1.1 6.5 L 1.1 7.1 L 0.5 7.1 C 0.279 7.1 0.1 7.279 0.1 7.5 C 0.1 7.721 0.279 7.9 0.5 7.9 L 1.1 7.9 L 1.1 8.5 C 1.1 8.721 1.279 8.9 1.5 8.9 C 1.721 8.9 1.9 8.721 1.9 8.5 L 1.9 7.9 L 2.5 7.9 C 2.721 7.9 2.9 7.721 2.9 7.5 C 2.9 7.279 2.721 7.1 2.5 7.1 L 1.9 7.1 L 1.9 6.5 Z"
		fill="currentColor"
		initial={{ scale: 0.5, opacity: 0 }}
		animate={{ scale: 1, opacity: 1 }}
		transition={{ duration: 1, delay: 0.6, type: 'spring' }}
	/>
	<motion.path
		d="M 8.544 0.982 L 8.246 0.942 C 8.033 0.918 7.907 1.166 8.029 1.342 C 8.17 1.545 8.3 1.756 8.418 1.974 C 8.919 2.905 9.203 3.969 9.203 5.1 C 9.203 8.372 6.822 11.088 3.699 11.61 C 3.457 11.65 3.21 11.677 2.96 11.691 C 2.746 11.702 2.627 11.954 2.777 12.107 C 2.846 12.177 2.915 12.246 2.987 12.313 L 3.059 12.38 L 3.319 12.605 L 3.507 12.753 L 3.628 12.843 L 3.815 12.974 L 3.991 13.089 C 4.112 13.165 4.235 13.238 4.362 13.306 L 4.625 13.441 L 4.886 13.561 L 5.188 13.683 L 5.432 13.769 C 5.566 13.813 5.701 13.853 5.839 13.889 C 5.943 13.916 6.048 13.94 6.154 13.962 C 6.28 13.988 6.407 14.011 6.535 14.03 L 6.852 14.069 L 7.119 14.089 C 7.246 14.097 7.374 14.1 7.503 14.1 C 11.148 14.1 14.103 11.145 14.103 7.5 C 14.103 7.25 14.089 7.004 14.062 6.761 L 14.026 6.484 C 13.991 6.261 13.945 6.041 13.889 5.826 C 13.821 5.567 13.738 5.314 13.641 5.069 L 13.528 4.801 L 13.451 4.635 L 13.377 4.487 C 13.218 4.178 13.035 3.883 12.831 3.604 L 12.678 3.404 L 12.479 3.164 L 12.316 2.984 L 12.196 2.86 L 12.036 2.702 L 11.813 2.502 L 11.493 2.242 L 11.248 2.065 L 10.956 1.874 L 10.635 1.689 L 10.307 1.524 L 10.194 1.472 L 9.955 1.371 L 9.678 1.267 L 9.426 1.185 L 9.101 1.095 L 8.836 1.035 L 8.544 0.982 Z"
		fill="currentColor"
		initial={{ scale: 0.8, opacity: 0, rotate: 30 }}
		animate={{ scale: 1, opacity: 1, rotate: 0 }}
		transition={{ duration: 1, type: 'spring', delay: 0.2 }}
	/>
	<motion.path
		d="M 10.403 5.3 C 10.403 4.276 10.2 3.298 9.832 2.406 C 11.762 3.29 13.103 5.239 13.103 7.5 C 13.103 10.593 10.596 13.1 7.503 13.1 C 6.636 13.1 5.816 12.904 5.084 12.552 C 6.542 12.094 7.811 11.208 8.743 10.042 C 8.88 10.228 9.1 10.35 9.349 10.35 C 9.763 10.35 10.099 10.014 10.099 9.6 C 10.099 9.243 9.85 8.944 9.515 8.868 C 9.577 8.752 9.637 8.633 9.693 8.513 C 9.884 8.632 10.109 8.7 10.35 8.7 C 11.04 8.7 11.6 8.14 11.6 7.45 C 11.6 6.76 11.041 6.2 10.351 6.2 C 10.385 5.905 10.403 5.605 10.403 5.3 Z"
		fill="none"
		initial={{ scale: 0.9, opacity: 0 }}
		animate={{ scale: 1, opacity: 1 }}
		transition={{ duration: 1, type: 'spring' }}
	/>
</svg>

Our icon is finally starting to look interesting. We have resolved all the issues with the previous version. Let us break down the changes made:

  1. A delay is added to everything, so we start with a blank space, 200ms later we get the first star appearing, 200s after we get the second star and the moon starts appearing, 200s later we get the final star animating in.
  2. A spring animation is added so that each element pops when it scales in size.
  3. The moon rotates by 30 degrees as it animates in to add a bit more flair.

Going above and beyond 🚀

There is more we could do to enhance the animation.

  1. Currently we are using the default values for a spring animation, they are normally great, but sometimes we might want to really dial the type of animation in.
  2. We can trigger the animation to happen on scroll vs on mount.
  3. Rotate the stars as they animate in.
  4. Give a slight rotation to the stars after they animate in

So far we have handled our animations to take place on mount. But we are not limited to that, we can have our animations play on all sorts of events such as: unmounting, hover, clicks. Armed with your new framer knowledge this is something you can explore. Here is a good place to start.

I hope you were able to learn something. Writing blog posts like these take a lot of time so I would really appreciate feedback and if you could share this.


Contact me 🤙

I would love to hear your thoughts on the article! Drop me a message.

Or come say hi on twitter.


Shoutouts 📣