Overview
Staggered animations consists of a series of sequential or overlapping animations, similar to a domino effect. Staggering animations is an effective way to add flare to animating a group of items. In the context of web animation, it can be done with the help of a JS animation plugin like GSAP, or simply with CSS, depending on the requirements.
Intro
In this post I will show how we can do it with SCSS. SCSS enable us to create staggered animations with its programming like features, namely variable, for loop, and calculations.
What staggered animations is, is a series of animations that are played in sequence with a tiny delay between each elements. We will need to calculate the delay needed for each elements and apply this delay to the transition-delay
or the animation-delay
property. The delay needs to be less than of the duration of the animation, so that the next animation will start before the current animation ended. Ideally the delay should be between 30% to 70% of the duration for the best effect.
Scenario
Given the scenario where there are 4 elements next to each other, and we want to fade in each elements with a duration of 300ms and a delay of 100ms between each other. Here are the HTML and CSS for the effect:
<ul class="list">
<li class="list-item">A</li>
<li class="list-item">B</li>
<li class="list-item">C</li>
<li class="list-item">D</li>
</ul>
.list-item {
opacity: 0;
animation: fade-in 300ms ease forwards;
}
.list-item:nth-child(2) {
animation-delay: 100ms;
}
.list-item:nth-child(3) {
animation-delay: 200ms;
}
.list-item:nth-child(4) {
animation-delay: 300ms;
}
@keyframes fade-in {
to {
opacity: 1;
}
}
2 key points from the above code:
-
opacity: 0
to hide the elements before they animate. -
forwards
as theanimation-fill-mode
value so that the end frame of the animation persists. In this scenario,fade-in
will bring the element's opacity to 1 and persist. Without this, element will disappear as soon as the animation ends.
SCSS
With SCSS we can achieve the same CSS output with the following code:
$n: 4;
.list-item {
opacity: 0;
animation: fade-in 300ms ease forwards;
@for $x from 2 through $n {
&:nth-child(#{$x}) {
animation-delay: 100ms * ($x - 1);
}
}
}
- We do a
@for
loop that starts from 2, and ends at 4. - Print / interpolate variable
$x
with#{}
to append it intonth-child()
. - Calculate the delay by multiplying 100ms with
($x - 1)
.
Do note that the number of loops is dependant on the number of HTML elements to be animated. This does make the SCSS solution to be inflexible if the number of HTML elements are dynamic. So depending on your needs, SCSS might not be enough. In cases where you can safely determine the number of HTML elements, this little snippet can prove to be a valuable tool to staggering animations. Areas where I have applied this technique are such as navigation menu and cover text animations.
Formula
The formula to calculating the delay can be the key to manipulating the staggered timing to get the ideal effect. Here are a few that I find commonly applicable:
- Reverse stagger
@for $x from 1 through ($n - 1) {
&:nth-child(#{$x}) {
animation-delay: ($n - $x) * 100ms;
}
}
- Ease-in stagger
@for $x from 2 through $n {
&:nth-child(#{$x}) {
animation-delay: 100ms * ($x - 1) / $x * $n;
}
}
- Ease-out stagger
@for $x from 2 through $n {
&:nth-child(#{$x}) {
animation-delay: 100ms * ($x - 1) / $n * $x;
}
}
Codepen
Here's a pen to show what's discussed in this post:
Application
Here are some of my pens where this technique is applied:
Happy animating!
Top comments (1)
Could you show how to make it loop? Like it plays 1 by 1 and repeats when the last has animated it restarts? Like staggered but it loops when the last one finishes. Is it possible only using scss? Without js.