DEV Community

Karthikeyan P
Karthikeyan P

Posted on • Edited on

How to animate horizontal bars sequentially in CSS3?

I usually use JavaScript to animate items on webpage sequentially. Recently, I have understood that I can use CSS3 keyframes to animate items sequentially. To demonstrate that, I animate bars (increasing width from left to right) sequentially here.

So, my HTML structure looks like this; Each bar is represented here in a div HTML element with a class bar.

<div class="bar-container">

  <div class="bar bar_0">

  </div>
  <div class="bar bar_1">

  </div>
  <div class="bar bar_2">

  </div>
  <div class="bar bar_3">

  </div>
  <div class="bar bar_4">

  </div>
  <div class="bar bar_5">

  </div>

</div>
Enter fullscreen mode Exit fullscreen mode

The bars are animated one after the other; the bars of different width and their actual bar animation duration depends on their width. I use SASS for this example.

I first set the width and height of the bar and their background as you can see below.

.bar {
  width: 100px; 
  height: 50px;
  background-color: rgba(66, 237, 148, 0.6);
}

Enter fullscreen mode Exit fullscreen mode

Now I set specific width to each bar using their unique CSS classname. As you can see each bars set different width and &.bar_5 is not defined styles because I want it to inherit width property from the common class .bar and other properties will be set later. I have used percentage value for the bar which is related to browser window width.

.bar {
  width: 100px;
  height: 50px;
  background-color: rgba(66, 237, 148, 0.6);

  &.bar_0 {
    width: 30%;
  }
  &.bar_1 {
    width: 50%;
  }
  &.bar_2 {
    width: 60%;
  }
  &.bar_3 {
    width: 70%;
  }
  &.bar_4 {
    width: 75%;
  }
}

Enter fullscreen mode Exit fullscreen mode

The above HTML and CSS together will render as.

Bars

Now the most interesting part, Animation. Since there are 6 bars we will have to have six different @keyframes to animate them sequentially. Let me show the @keyframes first then I explain them in details.

$percentages: (30, 50, 60, 65, 75, 100);
$factor: 20;
$interval: 0.5;
$previousAnimation: 0;
@for $i from 1 through 6 {
  @keyframes bar_animate_#{$i - 1} {
    @if $i > 2      
     {
      $previousAnimation: $previousAnimation + ($factor * nth($percentages, $i - 1) / 100) + $interval;
      0%, #{($previousAnimation)}% {
      transform: scaleX(0);
      transform-origin: left;
     } 
    }
    @else if $i == 2{
      $previousAnimation: $previousAnimation + ($factor * nth($percentages, $i - 1) / 100) + $interval;
      0%, #{$previousAnimation }%
      {
      transform: scaleX(0);
      transform-origin: left;
      }
    }
    @else
    {
      0%
      {
      transform: scaleX(0);
      transform-origin: left;
      }
    }

    #{$previousAnimation + ($factor * nth($percentages, $i) / 100)}%, 100%
      {
      transform: scaleX(1);
      transform-origin: left;
      }
  }
}
Enter fullscreen mode Exit fullscreen mode

Basically, I animate from scaleX(0) to scaleX(1) with transform-origin. The reason to have transform-origin is to tell transform property to start the scaling from left rather than from center (by default).

The important part is animation starting timing; it is represented here in %(percentage). Let's add animation to bar elements.

.bar {
  width: 100px;
  height: 50px;
  background-color: rgba(66, 237, 148, 0.6);
  animation-duration: 10s; //Added now

  &.bar_0 {
    width: 30%;
    animation-name: bar_animate_0; //Added now
  }
  &.bar_1 {
    width: 50%;
    animation-name: bar_animate_1; //Added now
  }
  &.bar_2 {
    width: 60%;
    animation-name: bar_animate_2; //Added now
  }
  &.bar_3 {
    width: 70%;
    animation-name: bar_animate_3; //Added now
  }
  &.bar_4 {
    width: 75%;
    animation-name: bar_animate_4; //Added now
  }
  &.bar_5{
    animation-name: bar_animate_5; //Added now
  }
}
Enter fullscreen mode Exit fullscreen mode

I have added animation-duration and animation-name; the overall animation duration is same for all element but actual bar animation duration and start timing are different. If you look closely I animate bar_0 from 0s to 0.6s (0% to 6% of 10s) and rest of the animation duration stays the same and also during this time, all other bars do not animate because they are set to transform: scaleX(0) which basically set width to 0 (hidden). Then, I give .5s (.5%) duration for next bar element to start animation then next bar animate for 1s and so the same logic goes till the last bar element.

This is how I was able to animate the elements sequentially with just CSS3 @keyframes.

CodePen DEMO

If you have any questions or suggestions please write below. Thanks

Top comments (3)

Collapse
 
washingtonsteven profile image
Steven Washington • Edited

Pretty cool!

There's also animation-delay, which is helpful for spacing out the timing of animations: developer.mozilla.org/en-US/docs/W...

Side note: one reason why I like preprocessors (my go to is Sass) is that necessarily repetitive CSS can be encapsulated in a for loop:

// note: using $factor1 and $factor2 for the multiplication factors to 
// determine the animation percentages
@for $i from 0 through 5 {
  @keyframes bar_animate_#{$i} {
    0%,
    #{$i * $factor1}% {
      transform: scaleX(0);
      transform-origin: left;
    }
    #{$i * $factor2}%,
    100% {
      transform: scaleX(1);
      transform-origin: left;
    }
  }
}

And similarly for the .bar_[i] classes.

One more thing, looks like the CSS in that last block is correct, you set all the animations to bar_animate_5. The Codepen is correct though.

Collapse
 
forethought_de profile image
Karthikeyan P

Thanks for suggesting improvement and pointing out the mistake. I have corrected the mistake now. I will refactor it in the evening.

Collapse
 
forethought_de profile image
Karthikeyan P

@Steven, I have refactored the animation to use Sass's for loop and updated the article. Many thanks for suggesting the improvement.