3

I have three spans with border at the bottom and need it fade in and fade out from top to bottom and reverse direction. It working fine if I have 2 iterations count but with infinite not working properly.

.line {
  border-bottom: 0.2em solid #333;
  display: block;
  margin: 0 auto;
  margin-top: 0.3em;
  width: 1.5em;
}
.lines span[class='line']:nth-child(1) {
  animation: 1.5s ease-in-out 0.2s 2 alternate fade_line;
}
.lines span[class='line']:nth-child(2) {
  animation: 1s ease-in-out 0.7s 2 alternate fade_line;
}
.line {
  animation: 0.5s ease-in-out 1.2s 2 alternate fade_line;
}
@keyframes fade_line {
  0%, 50% {
    border-bottom: 0.2em solid #333;
  }
  50%,
  100% {
    border-bottom: 0.2em solid #ddd;
  }
}
<div class="lines">
  <span class="line"></span>
  <span class="line"></span>
  <span class="line"></span>
</div>

Fiddle Demo

Harry
  • 87,580
  • 25
  • 202
  • 214
DimmuBoy
  • 103
  • 7
  • 1
    I am a bit lost on how to fix this but the reason for the problem is because of how `animation-delay` works. The delay is added only for the first time. So the animations overlap. You may find [this thread](http://stackoverflow.com/questions/32223835/repeat-animation-every-3-seconds/32223950#32223950) a bit helpful in finding a solution. – Harry Jan 06 '16 at 14:32
  • I couldn't find a way to fix that animation of yours but [**here**](https://jsfiddle.net/70w8mLq4/3/) is a piece of code that produces something similar. Not sure if it is of any help to you. – Harry Jan 06 '16 at 14:55

2 Answers2

3

The problem with the animation is that the animation-delay actually applies only to the first iteration of an animation. The below is an explanation of why it works when there are only 2 iterations and why it doesn't when there are infinite iterations.

Why does it work when there are only 2 iterations?

  • First span starts its animation at 0.2s (after animation-delay). Between 0.2s to 1.7s, the border color goes from #333 to #ddd (at 0.95s which is 50% of 1.5s duration to be precise) and between 1.7s and 3.2s it goes back (reverse direction) from #ddd to #333 (at 2.45s to be precise).
  • Second span starts its animation at 0.7s. Between 0.7s to 1.7s, the border color goes from #333 to #ddd (at 1.2s to be precise) and between 1.7s to 2.7s it goes back (reverse) from #ddd to #333 (at 2.2s to be precise).
  • Third span starts its animation at 1.2s. Between 1.2s to 1.7s, the border color goes from #333 to #ddd (at 1.45s to be precise) and between 1.7s to 2.2s it goes back (reverse) from #ddd to #333 (at 1.95s to be precise).
Element         | Iteration 1       | Iteration 2
---------------------------------------------------
First Span      | 0.95s             | 2.45s
Second Span     | 1.2s              | 2.2s
Third Span      | 1.45s             | 1.95s

As you can see, there is a nice flow to it as far as there are only 2 iterations.


Why does it not work when the iteration count is set to infinite?

Now lets see what happens with the third and subsequent iterations:

  • First span - Between 3.2s to 4.7s, the border color goes from #333 to #ddd (at 3.95s to be precise) and between 4.7s and 6.2s it goes back (reverse) from #ddd to #333 (at 5.45s to be precise).
  • Second span - Between 2.7s to 3.7s the border color goes from #333 to #ddd (at 3.2s to be precise) and between 3.7s to 4.7s it goes back (reverse) from #ddd to #333 (at 4.2s to be precise).
  • Third span - Between 2.2s to 2.7s the border color goes from #333 to #ddd (at 2.45s to be precise) and between 2.7s to 3.2s it goes back (reverse) from #ddd to #333 (at 2.95s to be precise).
Element         | Iteration 1       | Iteration 2       | Iteration 3       | Iteration 4
------------------------------------------------------------------------------------------
First Span      | 0.95s             | 2.45s             | 3.95s             | 5.45s
Second Span     | 1.2s              | 2.2s              | 3.2s              | 4.2s
Third Span      | 1.45s             | 1.95s             | 2.45s             | 2.95s

As you can see the flow is completely messed up because the animations start to overlap due to the way the animation duration and delay are configured.


Solutions:

Generally, the approach for introducing a delay between each iteration of an infinite loop animation is to modify the keyframes such that they add an equal delay for every iteration. I had explained this in my answer here. Unfortunately, your case is a lot more complex due to reverse animation. I couldn't modify the keyframes to match your expectation but I hope you'd find the abov explanation helpful in understanding the issue.

If you are happy to use alternate methods to achieve the same effect then you could have a look at linear-gradient background images to create the hamburger effect and then add animation to it.

.lines {
  margin: 0 auto;
  height: 30px;
  width: 30px;
  background-image: linear-gradient(#333, #333), linear-gradient(#333, #333), linear-gradient(#333, #333);
  background-size: 100% 5px;
  background-position: 0px 5px, 0px 15px, 0px 25px;
  background-repeat: no-repeat;
  animation: bars 1.7s infinite alternate ease-in-out;
}
@keyframes bars {
  0% {
    background-image: linear-gradient(#333, #333), linear-gradient(#333, #333), linear-gradient(#333, #333);
  }
  33% {
    background-image: linear-gradient(#ddd, #ddd), linear-gradient(#333, #333), linear-gradient(#333, #333);
  }
  66% {
    background-image: linear-gradient(#ddd, #ddd), linear-gradient(#ddd, #ddd), linear-gradient(#333, #333);
  }
  100% {
    background-image: linear-gradient(#ddd, #ddd), linear-gradient(#ddd, #ddd), linear-gradient(#ddd, #ddd);
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class="lines"></div>

Below is a solution using your initial approach (kindly contributed by vals). But you can't reuse the keyframes!

.line {
  border-bottom: 0.2em solid #333;
  display: block;
  margin: 0 auto;
  margin-top: 0.3em;
  width: 1.5em;
}
.line {
  animation: 1.7s ease-in-out infinite alternate fade_line1;
}
.line:nth-child(2) {
  animation-name: fade_line2;
}
.line:nth-child(3) {
  animation-name: fade_line3;
}
@keyframes fade_line1 { /* use 25% instead of 30% if the splits need to be equal among all 3 */
  0%, 30% {
    border-bottom-color: #333;
  }
  30%, 100% {
    border-bottom-color: #ddd;
  }
}
@keyframes fade_line2 {
  0%, 50% {
    border-bottom-color: #333;
  }
  50%, 100% {
    border-bottom-color: #ddd;
  }
}
@keyframes fade_line3 { /* use 75% instead of 70% if the splits need to be equal among all 3 */
  0%, 70% {
    border-bottom-color: #333;
  }
  70%, 100% {
    border-bottom-color: #ddd;
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class="lines">
  <span class="line"></span>
  <span class="line"></span>
  <span class="line"></span>
</div>
Community
  • 1
  • 1
Harry
  • 87,580
  • 25
  • 202
  • 214
  • 1
    That's great! Thank you for solution and explanation too. I tried to draw each keyframe on paper for better understanding :) – DimmuBoy Jan 06 '16 at 22:32
0

After using to much time, and effort i wanted to create a (simple) alternative answer using svg.

After an hour of javascript coding, I am going to post how it is so far. I'm not satisfied with the code, since it is to complex and using quite a some javascript code to animate the color transition.

var stop = document.getElementById("stop2");

var dir = false;
var amount = 1;
var pause = false;

startanim();

function fade() {
  if (amount >= 100) {
    if (amount == 150) {
      dir = true;
    }
    amount++;
  } else if (amount <= 0) {
    if (amount == -50) {
      dir = false;
    }
    amount--;
  }
  if (dir) {
    stop.setAttribute("offset", amount + "%");
    amount--;
  } else {
    stop.setAttribute("offset", amount + "%");
    amount++;
  }
}

var interval = "id";

function startanim() {
  interval = setInterval(function() {
    fade();
  }, 10);
}
.handle {
  mask: url(#myMask);
}
<svg id="burger" viewBox="0 0 50 50" width="100" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <linearGradient id="Gradient1" x1="0" x2="0" y1="0" y2="1">
      <stop id="stop1" stop-color="black" offset="0%" />
      <stop id="stop2" stop-color="#ddd" offset="1%" />
      <stop id="stop4" stop-color="#ddd" offset="100%" />
    </linearGradient>
    <mask id="myMask" maskUnits="userSpaceOnUse" x="0" y="0" width="50" height="50">
      <rect x="0" y="0" width="50" height="50" fill="url(#Gradient1)" />
    </mask>
  </defs>
  <rect class="handle" x="20" y="12" width="30" height="5" />
  <rect class="handle" x="20" y="25" width="30" height="5" />
  <rect class="handle" x="20" y="37" width="30" height="5" />
</svg>
Persijn
  • 14,624
  • 3
  • 43
  • 72