Wednesday, October 15, 2025
HomeProgrammingSequential linear() Animation With N Components

Sequential linear() Animation With N Components


Let’s suppose you’ve gotten N parts with the identical animation that ought to animate sequentially. The primary one, then the second, and so forth till we attain the final one, then we loop again to the start. I’m positive what I’m speaking about, and also you additionally know that it’s tough to get such an impact. It’s essential outline complicated keyframes, calculate delays, make it work for a particular variety of gadgets, and so forth.

Let you know what: with fashionable CSS, we will simply obtain this utilizing just a few strains of code, and it really works for any variety of gadgets!

The next demo is at the moment restricted to Chrome and Edge, however will work in different browsers because the sibling-index() and sibling-count() features acquire broader assist. You possibly can monitor Firefox assist in Ticket #1953973 and WebKit’s place in Problem #471.

Within the above demo, the weather are animated sequentially and the keyframes are so simple as a single to body altering a component’s background colour and scale:

@keyframes x {
  to {
    background: #F8CA00;
    scale: .8;
  }
}

You possibly can add or take away as many gadgets as you need and the whole lot will maintain working easily. Cool, proper? That impact is made attainable with this unusual and complex-looking code:

.container > * {
  --_s: calc(100%*(sibling-index() - 1)/sibling-count());
  --_e: calc(100%*(sibling-index())/sibling-count());

  animation: 
    x calc(var(--d)*sibling-count()) infinite 
    linear(0, 0 var(--_s), 1, 0 var(--_e), 0);
}

It’s a bit scary and unreadable, however I’ll dissect it with you to grasp the logic behind it.

The CSS linear() perform

When working with animations, we will outline timing features (additionally known as easing features). We will use predefined key phrase values — comparable to linear, ease, ease-in, and so forth. — or steps() to outline discrete animations. There’s additionally cubic-bezier().

However we now have a more recent, extra highly effective perform we will add to that record: linear().

From the specification:

A linear easing perform is an easing perform that interpolates linearly between its management factors. Every management level is a pair of numbers, associating an enter progress worth to an output progress worth.

animation-timing-function: linear creates a linear interpolation between two factors — the begin and finish of the animation — whereas the linear() perform permits us to outline as many factors as we wish and have a “linear” interpolation between two consecutive factors.

It’s a bit complicated at first look, however as soon as we begin working with it, issues turns into clearer. Let’s begin with the primary worth, which is nothing however an equal of the linear worth.

linear(0 0%, 1 100%)

We’ve two factors, and every level is outlined with two values (the “output” progress and “enter” progress). The “output” progress is the animation (i.e., what’s outlined inside the keyframes) and the “enter” progress is the time.

Let’s contemplate the next code:

.field {
  animation: transfer 2s linear(0 0%, 1 100%);
}

@keyframes transfer {
  0%   {translate: 0px }
  100% {translate: 80px}
}

On this case, we wish 0 of the animation (translate: 0px) at t=0% (in different phrases, 0% of 2s, so 0s) and 1 of the animation (translate: 80px) at t=100% (which is 100% of 2s, so 2s). Between these factors, we do a linear interpolation.

As an alternative of percentages, we will use numbers, which implies that the next can also be legitimate:

linear(0 0, 1 1)

However I like to recommend you follow the proportion notation to keep away from getting confused with the primary worth which is a quantity as effectively. The 0% and 100% are implicit, so we will take away them and easily use the next:

linear(0, 1)

Let’s add a 3rd level:

linear(0, 1, 0)

As you may see, I’m not defining any “enter” progress (the proportion values that signify the time) as a result of they aren’t necessary; nonetheless, introducing them is the very first thing to do to grasp what the perform is doing.

The primary worth is all the time at 0% and the final worth is all the time at 100%.

linear(0 0%, 1, 0 100%)

The worth will likely be 50% for the center level. When a management level is lacking its “enter” progress, we take the mid-value between two adjoining factors. In case you are conversant in gradients, you’ll discover the identical logic applies to paint stops.

linear(0 0%, 1 50%, 0 100%)

Simpler to learn, proper? Are you able to clarify what it does? Take a couple of minutes to consider it earlier than persevering with.

Received it? I’m positive you probably did!

It breaks down like this:

  1. We begin with translate: 0px at t=0s (0% of 2s).
  2. Then we transfer to translate: 80px at t=1s (50% of 2s).
  3. Then we get again to translate: 0px at t=2s (100% of 2s).

A lot of the timing features enable us to solely transfer ahead, however with linear() we will transfer in each instructions as many occasions as we wish. That’s what makes this perform so highly effective. With a “easy” keyframes you may have a “complicated” animation.

I may have used the next keyframes to do the identical factor:

@keyframes transfer {
  0%, 100% { translate: 0px }
  50% { translate: 80px }
}

Nevertheless, I received’t be capable of replace the proportion values on the fly if I need a completely different animation. There isn’t a strategy to management keyframes utilizing CSS so I must outline new keyframes every time I want a brand new animation. However with linear(), I solely want one keyframes.

Within the demo beneath, all the weather are utilizing the identical keyframes and but have fully completely different animations!

Add a delay with linear()

Now that we all know extra about linear(), let’s transfer to the primary trick of our impact. Don’t overlook that the concept is to create a sequential animation with a sure quantity (N) of parts. Every ingredient must animate, then “wait” till all of the others are accomplished with their animation to start out once more. That ready time will be seen as a delay.

The intuitive manner to do that is the next:

@keyframes transfer {
  0%, 50% { translate: 0px }
  100% { translate: 80px }
}

We specify the identical worth at 0% and 50%; therefore nothing will occur between 0% and 50%. We’ve our delay, however as I mentioned beforehand, we received’t be capable of management these percentages utilizing CSS. As an alternative, we will categorical the identical factor utilizing linear():

linear(0 0%, 0 50%, 1 100%)

The primary two management factors have the identical “output” progress. The primary one is at 0% of the time, and the second at 50% of the time, so nothing will “visually” occur within the first half of the animation. We created a delay with out having to replace the keyframes!

@keyframes transfer {
  0% { translate: 0px }
  100% { translate: 80px }
}

Let’s add one other level to get again to the preliminary state:

linear(0 0%, 0 50%, 1 75%, 0 100%)

Or just:

linear(0, 0 50%, 1, 0)

Cool, proper? We’re in a position to create a posh animation with a easy set of keyframes. Not solely that, however we will simply regulate the configuration by tweaking the linear() perform. That is what we’ll do for every ingredient to get our sequential animation!

The total animation

Let’s get again to our first animation and use the earlier linear() worth we did earlier than. We are going to begin with two parts.

Nothing shocking but. Each parts have the very same animation, so that they animate the identical manner on the identical time. Now, let’s replace the linear() perform for the primary ingredient to have the other impact: an animation within the first half, then a delay within the second half.

linear(0, 1, 0 50%, 0)

This actually inverts the earlier worth:

Tada! We’ve established a sequential animation with two parts! Are you beginning to see the concept? The objective is to do the identical with any quantity (N) of parts. After all, we’re not going to assign a distinct linear() worth for every ingredient — we’ll do it programmatically.

First, let’s draw a determine to grasp what we did for 2 parts.

Two square graphs fside by side showing the lines of the first two items. It's the same upward pointing spike, only shifting along the x-axis as you compare the graphs.

When one ingredient is ready, the opposite one is animating. We will determine two ranges. Let’s think about the identical with three parts.

Three square graphs from right to left showing the lines of the first three items. It's the same upward pointing spike, only shifting along the x-axis as you compare the graphs.

This time, we want three ranges. Every ingredient animates in a single vary and waits in two ranges. Do you see the sample? For N parts, we want N ranges, and the linear() perform can have the next syntax:

linear(0, 0 S, 1, 0 E, 0)

The begin and the finish are equal to 0, which is the preliminary state of the animation, then we now have an animation between S and E. A component will wait from 0% to S, animate from S to E, then wait once more from E to 100%. The animation time equals to 100%/N, which implies E - S = 100%/N.

The primary ingredient begins its animation on the first vary (0 * 100%/N), the second ingredient on the second vary (1 * 100%/N), the third ingredient on the third vary (2 * 100%/N), and so forth. S is the same as:

S = (i - 1) * 100%/N

…the place i is the index of the ingredient.

Now, you might ask, how will we get the worth of N and i? The reply is so simple as utilizing the sibling-count()and sibling-index() features! Once more, these are at the moment supported in Chromium browsers, however we will count on them to roll out in different browsers down the highway.

S = calc(100%*(sibling-index() - 1)/sibling-count())

And:

E = S + 100%/N
E = calc(100%*sibling-index()/sibling-count())

We write all this with some good CSS and we’re accomplished!

.field {
  --d: .5s; /* animation period */
  --_s: calc(100%*(sibling-index() - 1)/sibling-count());
  --_e: calc(100%*(sibling-index())/sibling-count());

  animation: x calc(var(--d)*sibling-count()) infinite linear(0, 0 var(--_s), 1, 0 var(--_e), 0);
}
@keyframes x {
  to {
    background: #F8CA00;
    scale: .8;
  }
}

I used a variable (--d) to manage the period, but it surely’s not necessary. I needed to have the ability to management the period of time every ingredient takes to animate. That’s why I multiply it later by N.

Now all that’s left is to outline your animation. Add as many parts as you need, and watch the end result. No extra complicated keyframes and magic values!

Word: For unknown causes (in all probability a bug) it is advisable register the variables with @property.

Extra variations

We will prolong the essential thought to create extra variations. For instance, as a substitute of getting to attend for a component to fully finish its animation, the subsequent one can already begin its personal.

This time, I’m defining N + 1 ranges, and every ingredient animates in two ranges. The primary ingredient will animate within the first and second vary, whereas the second ingredient will animate within the second and third vary; therefore an overlap of each animations within the second vary, and so forth.

I can’t spend an excessive amount of time explaining this case as a result of it’s one instance amongst many we create, so I allow you to dissect the code as a small train. And right here is one other one so that you can research as effectively.

Conclusion

The linear() perform was primarily launched to create complicated easing comparable to bounce and elastic, however mixed with different fashionable options, it unlocks loads of prospects. By this text, we acquired a small overview of its potential. I mentioned “small” as a result of we will go additional and create much more complicated animations, so keep tuned for extra articles to return!

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments