A week ago I published a post on how to make a spinner in pure CSS. This time, I'm going to make something slightly more complicated: A Pulse Loader.
The HTML
The HTML for this is rather straight forward. All we need is a container element with two child elements to act as the ripples.
<div class="loader">
<div class="pulse pulse1"></div>
<div class="pulse pulse2"></div>
</div>
We give the container element a class of .loader
. Then we set the class .pulse
on both of the child elements together with their individual class names, .pulse1
and .pulse2
.
Having a shared class name for the child elements will make the CSS a bit easier since we only have to apply styles to one class. We can then control the individual pulse animations using the .pulse1
and .pulse2
class names.
And that's all the HTML we'll need.
The CSS
The CSS is going to be a bit more complicated this time.
The container
First, we need to set the container styles.
We're going to set the width
and height
of the container to equal values. This will be important later when we set the border-radius
. If the width
and height
aren't equal, the pules would be ovals instead of circles.
.loader {
/* Make sure it's a circle */
width: 32px;
height: 32px;
}
Now, set the border-radius
to actually make it be a circle.
.loader {
/* Make it be a circle */
border-radius: 50%;
}
We also need to set the position
of the container to relative
. Our .pulse
class will later get a position
of absolute
and having their container be relative
will make sure they stay within its bounds.
.loader {
/* Make sure the children will behave */
position: relative;
}
The last thing for the container is to give it some space to breathe. You can change this to whatever values you like, but keep in mind that the ripples will grow to twice the width and height. If we don't give it enough space, the ripples could be cut off.
.loader {
/* Give it some space */
margin: 2rem auto;
}
The pulses
As you can tell from the HTML, we're going to have two pulses. The styles of the pulses will be the same. The only thing that differs is the animation-delay but we'll get to that later.
First, create a border for the pulse. You can choose whatever color or thickness you want.
.loader .pulse {
/* The color and thickness of the pulse */
border: 3px solid blue;
}
Next, we'll make the pulses have the same starting position. We can do this by setting position: absolute;
. That'll make sure our pulses aren't stacked on top of each other.
.loader .pulse {
/* Make sure the pulses don't interfere */
position: absolute;
}
Earlier, we specified the width
and height
of the loading container. Now, we'll want the pulses to have that same width
and height
. To achieve that, we can simply set both rules to 100%
.
.loader .pulse{
/* Fill up the parent */
width: 100%;
height: 100%;
}
We also want the pulses to be round, like their parent. So, we can just set the border-radius
to inherit its value from the parent.
.loader .pulse{
/* Make it round */
border-radius: inherit;
}
To give the animation a nicer effect, we'll also scale down the pulses slightly.
.loader .pulse{
/* Scale down */
transform: scale(0.5);
}
The last thing we'll do on the styles for the pulses is to set a transition for it's opacity. We do this because when the animation starts over we don't want the pulse to immediately appear. We want it to look smooth.
.loader .pulse{
/* Make it smooth */
transition: opacity 200ms linear;
}
That is all the actual styling we need.
The animation
Now, it's time to create the animation to make it look like it's pulsing.
Remember that we added individual class names to the pulses? We'll use those now!
The first pulse, .pulse1
, should start immediately and repeat infinitely. Meanwhile, the second pulse, .pulse2
, should start after a slight delay. This is done by specifying two numeric values to the animation property.
First pulse:
.loader .pulse1{
animation: pulse 1s infinite;
}
Second pulse:
.loader .pulse2{
animation: pulse 1s 500ms linear infinite;
}
Notice that we also set the linear
timing function on the second pulse. The first pulse, however, uses the default timing function, ease
, which means it will be faster towards the middle of the animation and slow down at the beginning and end. We do this to get a more interesting effect.
Now, the animation itself.
@keyframes pulse{
to{
transform: scale(2);
opacity: 0;
}
}
The animation is simple. We set a to
value, which determines the state at the end of the animation, to be completely transparent and grow to twice its original size.
This will make it seem like the pulses disappear out into the void and new ones appear to grow from the middle.
And we're all done. We should now have a repeating pulse effect.
Putting it all together
Let's put everything together.
The HTML:
<div class="loader">
<div class="pulse pulse1"></div>
<div class="pulse pulse2"></div>
</div>
The CSS:
.loader{
/* Make sure it's a circle */
width: 32px;
height: 32px;
/* Make it be a circle */
border-radius: 50%;
/* Make sure the children will behave */
position: relative;
/* Give it some space */
margin: 2rem auto;
}
.loader .pulse{
/* The color and thickness of the pulse */
border: 3px solid blue;
/* Make sure the pulses don't interfere */
position: absolute;
/* Fill up the parent */
width: 100%;
height: 100%;
/* Make it round */
border-radius: inherit;
/* Scale down */
transform: scale(0.5);
/* Make it smooth */
transition: opacity 200ms linear;
}
.loader .pulse1{
animation: pulse 1s infinite;
}
.loader .pulse2{
animation: pulse 1s 500ms linear infinite;
}
@keyframes pulse{
to{
transform: scale(2);
opacity: 0;
}
}
Conclusion
Even though this pulse effect is slightly more complicated than the spinner we did last week, it is still fairly simple to create.
You can, of course, customize this effect in a number of ways. You could change the colors of the pulses or even use different colors for each of them. You could change the animation timing functions to get interesting variations. Or you could simply add more pulses.
Next week, I'll create a skeleton loader. If you don't want to miss that, consider subscribing to my newsletter here on Hashnode. You could also follow me on Twitter(@chj_web).