3

I am trying to build a programmable css only countdown. Tens of the second is working good but ones of it is not working as I expected.

*:after{
 animation-direction: reverse;
 animation-iteration-count: infinite;
 -webkit-animation-timing-function: linear; /* Safari 4.0 - 8.0 */
  animation-timing-function: linear;
}
.secondsTen:after{
 content:'';
 animation-name: tenBased;
 animation-duration: 10s;
}

.secondsSix:after{
 content:'';
 animation-name: sixBased;
 animation-duration: 60s;
}

@keyframes tenBased {
 0% { content: '0';}
 10% { content: '1';}
 20% { content: '2';}
 30% { content: '3';}
 40% { content: '4';}
 50% { content: '5';}
 60% { content: '6';}
 70% { content: '7';}
 80% { content: '8';}
 90% { content: '9';}
 100% { content: '0';}
}

@keyframes sixBased {
  0% {content: '0';}
  16.66667% {content: '1';}
  33.33333% {content: '2';}
  50% {content: '3';}
  66.66667% {content: '4';}
  83.33333% {content: '5';}
  100% {content: '0';}
}
<span class='secondsSix'></span>
<span class='secondsTen'></span>

As you can see, ones of the second is decreasing after 5 seconds. But it supposed to decrease after 10 seconds to 5.

Ahmet Can Güven
  • 5,392
  • 4
  • 38
  • 59

2 Answers2

2

Explanation of the observed behaviour

The keyframe animation performs a transition between the given states. In your example you animate the value of the content property of a pseudo-element which is totally valid but you have to keep in mind that there are no intermediate steps between two keyframes that can be visualized in this case.

So in fact, the transition from 100% {content: '0';} to 83.33333% {content: '5';} will end after 10 seconds (as expected) but at some point in between the value of the property changes. When setting animation-timing-function: linear; it's exactly between the two keyframes - after 5 seconds in this example what makes it look like it starts with a delay.

*:after{
  animation-direction: reverse;
  animation-iteration-count: infinite;
  -webkit-animation-timing-function: linear; /* Safari 4.0 - 8.0 */
  animation-timing-function: linear;
}

.secondsSix:after{
  content:'';
  animation-name: sixBased;
  animation-duration: 60s;
  position: absolute;
}

@keyframes sixBased {
  0% {content: '0'; left: 100%}
  16.66667% {content: '1';}
  33.33333% {content: '2';}
  50% {content: '3';}
  66.66667% {content: '4';}
  83.33333% {content: '5';}
  100% {content: '0'; left: 0%}
}

.timeline {
  border: solid grey;
  border-width: 1px 0;
  background: white;
  position: absolute;
  width: 100%;
  left: 0;
  top: 2em;
}

.timeline span {
  display: inline-block;
  float: left;
  margin-right: 8.33%;
  width: 0;
}
<div class='secondsSix'></div>
<div class='timeline'>
  <span>0</span><span>5</span><span>10</span><span>15</span><span>20</span><span>25</span>
  <span>30</span><span>35</span><span>40</span><span>45</span><span>50</span><span>55</span>
</div>

I've analyzed the behaviour with the help of the above snippet and had a look at what will happen if I remove individual keyframes (one at a time, represented by a gap in the following table).

Content 0   5   4   3   2   1   0
Time    0   5   15  25  35  45  55
Time    0       10  25  35  45  55
Time    0   5       20  35  45  55
Time    0   5   15      30  45  55 // change from '4' to '2' at second 30
Time    0   5   15  25      40  55
Time    0   5   15  25  35      50

As you can see, the content always changes exactly between two given keyframes. The same can be observed in the following demo where I've modified your tenBase animation:

*:after {
  animation-direction: reverse;
  animation-iteration-count: infinite;
  -webkit-animation-timing-function: linear; /* Safari 4.0 - 8.0 */
  animation-timing-function: linear;
  animation-duration: 10s;
}

.secondsTen:after {
  content: '';
  animation-name: tenBased;
}

.secondsTenTwoSteps:after {
  content: '';
  animation-name: tenBasedTwoSteps;
}

@keyframes tenBased {
  0% { content: '0' }
  10% { content: '1'; }
  20% { content: '2'; }
  30% { content: '3'; }
  40% { content: '4'; }
  50% { content: '5'; }
  60% { content: '6'; }
  70% { content: '7'; }
  80% { content: '8'; }
  90% { content: '9'; }
  100% { content: '10' }
}

@keyframes tenBasedTwoSteps {
  0% { content: '0' }
  100% { content: '10' }
}
<div class='secondsTen'></div>
<div class='secondsTenTwoSteps'></div>

Solution #1

The first way of solving the problem is simple but not very beautiful. What you want is to change a property in a CSS animation abruptly without animating and what the OP describes as hacky way in the linked question would look like this:

*:after{
  animation-direction: reverse;
  animation-iteration-count: infinite;
  -webkit-animation-timing-function: linear; /* Safari 4.0 - 8.0 */
  animation-timing-function: linear;
}

.secondsSix:after{
  content:'';
  animation-name: sixBased;
  animation-duration: 60s;
  position: absolute;
}

@keyframes sixBased {
  0% {content: '0'; left: 100%}
  0.1% {content: '0';}
  16.66667% {content: '1';}
  16.7% {content: '1';}
  33.3% {content: '2';}
  33.33333% {content: '2';}
  33.4% {content: '3';}
  50% {content: '3';}
  50.1% {content: '4';}
  66.66667% {content: '4';}
  66.7% {content: '5';}
  83.33333% {content: '5';}
  83.4% {content: '0';}
  100% {content: '0'; left: 0%}
}

.timeline {
  border: solid grey;
  border-width: 1px 0;
  background: white;
  position: absolute;
  width: 100%;
  left: 0;
  top: 2em;
}

.timeline span {
  display: inline-block;
  float: left;
  margin-right: 8.33%;
  width: 0;
}
<div class='secondsSix'></div>
<div class='timeline'>
  <span>0</span><span>5</span><span>10</span><span>15</span><span>20</span><span>25</span>
  <span>30</span><span>35</span><span>40</span><span>45</span><span>50</span><span>55</span>
</div>

Solution #2

There is also another solution. You'll see that changing animation-timing-function: linear; to something else like animation-timing-function: ease-out; changes the point in time when the content acutally changes. We have observed that the content changes exactly in the middle when using a linear timing function, so I assume that it will change on 50% of progress between two keyframes.

As it is allowed to define a cubic-bezier we can shift this point in time where the progress becomes 50% close to the end. Keep in mind that your animation-direction is reversed so you have to mirror the curve (see cubic-bezier.com/#0,1,0,1).

*:after{
  animation-direction: reverse;
  animation-iteration-count: infinite;
  -webkit-animation-timing-function: linear; /* Safari 4.0 - 8.0 */
}

.secondsSix:after{
  content:'';
  animation-name: sixBased;
  animation-duration: 60s;
  position: absolute;
}

@keyframes sixBased {
  0% {content: '0'; left: 100%}
  16.66667% {content: '1'; animation-timing-function: cubic-bezier(0,1,0,1);}
  33.33333% {content: '2'; animation-timing-function: cubic-bezier(0,1,0,1);}
  50% {content: '3'; animation-timing-function: cubic-bezier(0,1,0,1);}
  66.66667% {content: '4'; animation-timing-function: cubic-bezier(0,1,0,1);}
  83.33333% {content: '5'; animation-timing-function: cubic-bezier(0,1,0,1);}
  100% {content: '0'; left: 0%}
}

.timeline {
  border: solid grey;
  border-width: 1px 0;
  background: white;
  position: absolute;
  width: 100%;
  left: 0;
  top: 2em;
}

.timeline span {
  display: inline-block;
  float: left;
  margin-right: 8.33%;
  width: 0;
}
<div class='secondsSix'></div>
<div class='timeline'>
  <span>0</span><span>5</span><span>10</span><span>15</span><span>20</span><span>25</span>
  <span>30</span><span>35</span><span>40</span><span>45</span><span>50</span><span>55</span>
</div>
Community
  • 1
  • 1
Marvin
  • 9,164
  • 3
  • 26
  • 44
0

Would you like to try this

.secondsTen:before {
    content: '9876543210';
    width:1ch;
    overflow:hidden;
    animation:tenBased 10s steps(10) infinite;
    float:left;
}
.secondsSix:before{
    content: '543210';
    width:1ch;
    overflow:hidden;
    float:left;
    animation:sixBased 60s steps(6) infinite;
}
@keyframes tenBased {
    0% {text-indent:0}
    100% {text-indent:-10ch;}
}
@keyframes sixBased {
    0% {text-indent:0}
    100% {text-indent:-6ch;}
}
<span class='secondsSix'></span>
<span class='secondsTen'></span>
Amal
  • 3,398
  • 1
  • 12
  • 20