Search code examples
htmlcssbackgroundcss-animationslinear-gradients

CSS: how to create an infinitely-moving repeating linear gradient?


So basically, I am trying to create a progress bar. In this example, I will just change the colors I was using to red, green and blue instead since that's obviously easier to understand than a load of hex values. Effectively, what I am going for is for the progress bar to have this RGB gradient background that gives the impression the gradient is moving from left to right, to signify that there is still activity (i.e. that the site hasn't frozen). I've tried a few things, starting with just setting background: linear-gradient(120deg, red, green, blue) and animating the background-position CSS property to simulate the gradient moving. However, once at the end of the animation, the progress bar jumped from being mostly blue (i.e. the end of the gradient), right back to green...I then tried manually-reflecting the gradient in the form rgbgr - i.e. background: linear-gradient(120deg, red, green, blue, green, red) and, while this looks better, there is still jumpiness. Finally, I tried using the repeating-linear-gradient CSS function - i.e. background: repeating-linear-gradient(120deg, red, green, blue, green, red). This is the closest to what I'm aiming for, but in the example, you can see the gradient colors 'jumping', rather than animating smoothly

html, body{
  height: 100%;
  background: #222;
  overflow: hidden;
}

body{
  display: flex;
  justify-content: center;
  align-items: center;
}

*{
  color: white;
  font-family: 'Tahoma', sans-serif;
}

#wrapper {
    height: 50px;
    width: 400px;
    position: relative;
    background: #131313;
}

p{
    text-align: center;
    position: absolute;
    width: 100%;
}

#bar {
    background: repeating-linear-gradient(120deg, red,green,blue, green, red);
    background-repeat:repeat-x;
    height: 100%;
    position: absolute;
    background-size: 400% 100%;
    -webkit-animation: AnimationName 3s linear infinite;
    -moz-animation: AnimationName 3s linear infinite;
    animation: AnimationName 3s linear infinite;
}

@-webkit-keyframes AnimationName {
    0%{background-position:100% 50%}
    100%{background-position:0% 50%}
}
@-moz-keyframes AnimationName {
    0%{background-position:100% 50%}
    100%{background-position:0% 50%}
}
@keyframes AnimationName {
    0%{background-position:100% 50%}
    100%{background-position:0% 50%}
}
<div id="wrapper">
    <div id="bar" style="width: 50%"></div>
    <p>Downloading 5 of 10</p>
  </div>

I've seen this effect on many sites before, so I assume it's possible in CSS. If anyone can point me in the right direction, I'd appreciate it. Thanks!


Solution

  • You need to run the animation a bit longer before looping back.

    @keyframes AnimationName {
        0%{background-position:100% 50%}
        100%{background-position:-33% 50%} /* instead of 0% 50% */
    }
    

    I also changed the gradient angle to 90deg because the initial value makes the start and end of the gradient not matching very well

    /* instead of 120deg */
    background: repeating-linear-gradient(90deg, red,green,blue, green, red); 
    

    html, body{
      height: 100%;
      background: #222;
      overflow: hidden;
    }
    
    body{
      display: flex;
      justify-content: center;
      align-items: center;
    }
    
    *{
      color: white;
      font-family: 'Tahoma', sans-serif;
    }
    
    #wrapper {
        height: 50px;
        width: 400px;
        position: relative;
        background: #131313;
    }
    
    p{
        text-align: center;
        position: absolute;
        width: 100%;
    }
    
    #bar {
        background: repeating-linear-gradient(90deg, red,green,blue, green, red);
        background-repeat:repeat-x;
        height: 100%;
        position: absolute;
        background-size: 400% 100%;
        animation: AnimationName 3s linear infinite;
    }
    
    @keyframes AnimationName {
        0%{background-position:100% 50%}
        100%{background-position:-33% 50%}
    }
    <div id="wrapper">
        <div id="bar" style="width: 50%"></div>
        <p>Downloading 5 of 10</p>
      </div>