8

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!

Temani Afif
  • 245,468
  • 26
  • 309
  • 415
CssProblem
  • 245
  • 3
  • 8

2 Answers2

7

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>
NearHuscarl
  • 66,950
  • 18
  • 261
  • 230
  • Awesome man. Simply setting the background position to -33% was enough. Weirdly, it doesn't seem to matter how many colours I used or what the angle of the gradient was...as long as the background x position was around -33%, it seemed to work...with 5 colour stops, 10, 12, whatever... – CssProblem Sep 08 '20 at 06:05
  • @CssProblem it won't work with any angle, only for 90deg .. related to understand the logic behind the numbers: https://stackoverflow.com/a/51734530/8620333 – Temani Afif Sep 08 '20 at 10:04
  • @TemaniAfif yes, you're right - you seem to get a slightly brighter strike if the isn't horizontal - still, I guess in the example I was using (with about 14 colour stops at 120deg), it still looks pretty seemless to the human eye – CssProblem Sep 08 '20 at 10:44
7

You can do it like below and it will work with any angle you want:

body {
  background: #222;
}

.wrapper {
  --d:100px;
  --angle:120deg; 
  --sinus:0.866; /* = sinus(angle) */
  
  height: 50px;
  width: 400px;
  position: relative;
  z-index:0;
  background: #131313;
  text-align: center;
  line-height:50px;
  color: white;
  margin:5px;
}

.wrapper::before {
  content:"";
  height: 100%;
  left:0;
  width:var(--w);
  position: absolute;
  z-index:-1;

  background: repeating-linear-gradient(var(--angle), red, green, blue, green, red var(--d));
  background-size: calc(var(--d)/var(--sinus)) 100%;
  animation: AnimationName 2s linear infinite reverse;
}

@keyframes AnimationName {
  0% {
    background-position: calc(var(--d)/var(--sinus)) 0;
  }
}
<div class="wrapper" style="--w:50%;">
  Downloading 5 of 10
</div>

<div class="wrapper" style="--w:70%;--d:200px;--angle:45deg;--sinus:0.707">
  Downloading 5 of 10
</div>

<div class="wrapper" style="--w:80%;--d:50px;--angle:-30deg;--sinus:0.5">
  Downloading 5 of 10
</div>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • Can you describe in more detail about `/* = sinus(angle) */` and how the `--sinus` value is arrived at? Is there a formula to determine this based on angle? Thanks! – RCNeil Dec 13 '21 at 21:25
  • 1
    @RCNeil It is `sin(120)` where the `120` is in degrees, not radians. (For those wondering, "sinus" is Latin for "sine", which is the full name for the `sin()` function in trigonometry.) – kevin628 Jan 24 '23 at 21:17