Some years ago, I made a tool for finding operational problems for customers of a major Danish telecom company (you can see it here, but it’s in Danish, so you might not care). One of the things that I thought put the service a step above most projects was the design, particularly the custom loading animation that was displayed while a user searched for problems at a particular location.
This custom animation was a little YouSee repair van speeding along to fix the customer’s problems. It was originally designed by Ronnie S. Jakobsen as a proof-of-concept placeholder, and it proved to be so popular that it was kept on.
As SVG becomes more common, I would expect to see more individualized loading animations, and in this article, I will share some techniques on how you can adapt pretty much any SVG icon to be a loading animation.
So, first off, we should have an icon to play with. Let’s try this one from The Noun Project as a starting point.
See the Pen
User Icon from Noun Project by Bryan Rasmussen (@bryanrasmussen)
on CodePen.
I have done some minimal changes to this in removing unneeded markup and giving the head and body parts of the icon descriptive IDs.
There are a couple things I don’t like about the icon:
<path id="head" fill="#000000" d="M49.732-0.239L49.732-0.239h-0.001C38.062-0.193,28.649,9.407,28.725,21.194 c-0.076,11.904,9.337,21.501,21.006,21.441h0h0.001c11.668,0.061,21.082-9.537,21.006-21.441C70.814,9.407,61.4-0.193,49.732-0.239z"></path>
Therefore, I have made some adjustments to the markup, which, of course, makes it look slightly different, but I think these differences aren’t really discernible for the average user.
See the Pen
User Icon from Noun Project, improved markup by Bryan Rasmussen (@bryanrasmussen)
on CodePen.
The most common form of loading animation is one that just repeats infinitely, turned on and off by some event in the page where it is shown, generally controlled by JavaScript.
If we wanted to turn the user icon into a loading animation, the easiest way would be to use an SVG <feFlood>
filter with an animation running indefinitely, as seen below.
See the Pen
User Icon filtered 1 by Bryan Rasmussen (@bryanrasmussen)
on CodePen.
The relevant change from the static icon is the addition of a filter:
<defs> <filter id="iconfilter" primitiveUnits="objectBoundingBox" > <feFlood flood-color="green"/> <feOffset> <animate attributeName="dy" from="1" to="0" dur="4s" repeatCount="indefinite" /> </feOffset> <feComposite operator="in" in2="SourceGraphic" /> <feComposite operator="over" in2="SourceGraphic" /> </filter> </defs>
And applying that filter to the group of graphical elements composing the icon:
<g id="userIcon" filter="url(#iconfilter)">
I guess I should probably describe how this filter works. If all you had in the filter was the <feFlood>
element, then you would basically just have a green square, as seen here:
See the Pen
User Icon Green Square by Bryan Rasmussen (@bryanrasmussen)
on CodePen.
That’s because the filter is put on the <g>
element holding both the paths. Therefore, the subregion the flood fills represents the width and height of the SVG itself.
objectBoundingBox
If you didn’t have the attribute primitiveUnits="objectBoundingBox"
on the filter, you would just see a green user icon, as seen here:
See the Pen
User Icon flooded 1 by Bryan Rasmussen (@bryanrasmussen)
on CodePen.
The way primitiveUnits
works is described here, but not that well. So, to clarify some points: when it is not set, it takes the default value of userSpaceOnUse
, which means that units used in the filter will be the same size of units on the object, generally 1 pixel.
The reason we want it to be objectBoundingBox
is because it sets values in relation to the object bounding box of the <g>
element when the filter is applied. This is especially useful because when using objectBoundingBox
, 0 is the start of the bounding box and 1 is the end, meaning that our animation can move from 0 to 1 or vice versa and be understood.
feOffset
worksThis is done using feOffset
, which creates an offset of the image. That offset image is basically just sitting at the same location as our underlying image. feOffset
is described in this MDN resource, but, again, not that well; you can find a better description in the specification.
In reading the spec, you can see that if the dx
and dy
of the feOffset
are not specified, then the default values will be 0. So if your offset looked like the following:
<feOffset dy="0" dx="0" />
Then you would once again have a green icon:
See the Pen
User Icon feoffset dy example 1 by Bryan Rasmussen (@bryanrasmussen)
on CodePen.
If you play around with the dy
attribute — for example, something like this:
<feOffset dy="0.4" />
You will see that the top part of the head is black and the rest of the icon has the green flood. If you increase that dy
to be 1, you will see a small line of green at the bottom.
So, if you animate dy
from 1 to 0, it will animate the offset from the bottom to the top, meaning the black offset image will go from being so offset it nearly covers the original one, to not being seen at all, repeating indefinitely.
Obviously, if you changed it from 0 to 1, it would be from the top down. And if you changed it to instead animate dx
from 1 to 0, it would animate right to left, and 0 to 1 would animate left to right. This would not be a pleasing effect with the user icon, but check out this arrow with an animated offset from left to right:
See the Pen
Arrow Icon filtered 1 by Bryan Rasmussen (@bryanrasmussen)
on CodePen.
In this example, we have changed the feFlood
color to be the darker color and the fill of the path to be green. You can see that the dx
is being animated from 1 to 0; if we hadn’t done that, it would look like it was the dark color that was “moving,” which is not the effect one wants in a loading animation.
feComposite
worksIf you didn’t have this:
<feComposite operator="in" in2="SourceGraphic" />
Then you would have a black icon and a green square would animate from the bottom of the graphic to cover the icon, as seen here:
See the Pen
User Icon flood 3 by Bryan Rasmussen (@bryanrasmussen)
on CodePen.
If you didn’t have this:
<feComposite operator="over" in2="SourceGraphic" />
You would see an animation, but the whole icon would be coming up from the bottom of its area — not just of the color filling the icon — as seen here:
See the Pen
User Icon flood 2 by Bryan Rasmussen (@bryanrasmussen)
on CodePen.
You can read about how feComposite
works here, but it basically combines images. By combining the in
and over
operators, we end up with the top-level image that has the animation run on it.
Now that we know enough about how this animation works (although not all, as SVG is a big standard), I think it would be useful to turn what we’ve learned into code so we can have a generic loading animation that will take any SVG paths as its basis.
I will use React to make a component, although obviously you could do the same thing in whichever framework you prefer, or even in vanilla JavaScript.
The following component makes a simple loader out of any paths it gets sent, and an instruction startAt
, which can take the string values left
, up
, or down
. If it is anything other than these values, the assumption is that the animation will run right to left.
It sets some default values for you, which you can, of course, override. It is assumed that in a real project, you might want to require some properties like the style, or to always start at the left and go right, etc.
The component also uses nested ternary expressions in the interest of compactness, but in a real-world environment, one might want to unpack these expressions for legibility.
Regardless, here we have a working example:
class Loader extends PureComponent { render() { const { styles = {}, speed = "3s", width = "100%", height = "100%", repeatCount = "indefinite", startAt } = this.props; const isHoriz = (startAt === "top" || startAt === "bottom"); const from = (startAt === "left" || startAt === "bottom") ? 0 : 1; const to = (from === 1) ? 0 : 1; const attName = (isHoriz) ? "dy" : "dx"; const fill = (styles.fill) ? styles.fill : (startAt === "left" || startAt === "bottom") ? "#fff" : "#000"; const flood = (styles.flood) ? styles.flood : (fill === "#fff") ? "#000" : "#fff"; const stroke = (styles.stroke) ? styles.stroke : "none"; return ( <svg xmlns="http://www.w3.org/2000/svg" width={ width } height={ height }> <defs> <filter id="fullFill" primitiveUnits="objectBoundingBox" x="0%" y="0%" width="100%" height="100%"> <feFlood x="0%" y="0%" width="100%" height="100%" floodColor={ flood } /> <feOffset> <animate attributeName={attName} from={from} to={to} dur={ speed } repeatCount={ repeatCount }/> </feOffset> <feComposite operator="in" in2="SourceGraphic" /> <feComposite operator="over" in2="SourceGraphic" /> </filter> </defs> <g fill={ fill } filter={ `url(#fullFill)` } stroke={ stroke }> { this.props.children } </g> </svg> ); } }
So, if we call that with something like the following:
<Loader startAt="left"> <rect x="0" width="100" height="10" /> </Loader>
We would get a rectangular loading icon going from black to white every three seconds. You can see it here:
See the Pen
React Simple Loading Icon by Bryan Rasmussen (@bryanrasmussen)
on CodePen.
In the same way, if we want to animate from the bottom to the top, we can do:
<Loader startAt="bottom"> <rect x="0" height="100" width="10" /> </Loader>
See here:
See the Pen
React Simple Loading Icon 2 by Bryan Rasmussen (@bryanrasmussen)
on CodePen.
And for the sake of completeness, here are a couple example counting-down loaders. Going from right to left:
See the Pen
React Simple Loading Icon 3 by Bryan Rasmussen (@bryanrasmussen)
on CodePen.
Going from top to bottom:
See the Pen
React Simple Loading Icon 4 by Bryan Rasmussen (@bryanrasmussen)
on CodePen.
And here is an example using one of our previous selections of paths being given to the loading icon (startAt="bottom"
):
See the Pen
React Simple Loading Icon Body by Bryan Rasmussen (@bryanrasmussen)
on CodePen.
Of course the component takes a number of other props. Their uses should be evident from the previous examples, but take a look at the CodePen below as a reminder. Here, the styles set are {{flood: "green", fill: "white"}}
See the Pen
React Simple Loading Icon Body Styles set by Bryan Rasmussen (@bryanrasmussen)
on CodePen.
You should note that if you set the Fill
to be transparent, then the flood will only flood the stroke, so you should probably define a stroke at that point.
You can also set a stroke color in the styles
object if you want the object to have a border that does not change with the loading filter. Here, we’ve adjusted the speed to six seconds.
See the Pen
React Simple Loading Icon Body Styles set by Bryan Rasmussen (@bryanrasmussen)
on CodePen.
Height and width, if they are not set, will default to 100 percent, thus filling the area available. In most cases, I suppose that these won’t need setting, but you might have a reason to. Of course, you can also do something silly like set the width to 50 percent, as seen here:
See the Pen
React Simple Loading Icon Body 50% by Bryan Rasmussen (@bryanrasmussen)
on CodePen.
Finally, the repeatCount prop
just says how often we want the animation to repeat, as per the SVG attribute.
You might consider changing this to just pass in a class as a prop instead of the styles, as seen in this CodePen:
See the Pen
React Simple Loading Icon Body with css class by Bryan Rasmussen (@bryanrasmussen)
on CodePen.
I haven’t included the minor code changes here to save space, and obviously in real life you would probably choose a color scheme more pleasing to the eye, but the usage is clear. At any rate, there is a large number of properties that can be set by CSS in SVG; they are listed in the specification, and this list of properties will increase with the implementation of SVG 2.
This use of a CSS class allows us to remove the fill
and stroke
properties and not have a style
object anymore — we’d just pass in the flood
property to define what color our paths get filled with. Obviously, this opens up the possibility to control what classes get set via our usual React state management methods.
So with this, we have a generic method for turning simple SVG graphics like icons into loading animations, plus an example React component that we can use with any collection of SVG paths as children. I hope you find it useful and it spurs you to experiment with SVG animations in your own applications.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to implement one-way and two-way data binding in Vue.js, using v-model and advanced techniques like defineModel for better apps.
Compare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.