Css-only countdown can't make numbers work together
I am trying to create programmable css just countdown. Tens of the second work well, but some of them do not work 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, some of the latter shrink after 5 seconds. But it should decrease after 10 seconds to 5.
source to share
Explanation of Observed Behavior
Activating a key frame transitions between these states. In your example, you are animating the property value of a content
pseudo - element, which is fully valid, but you must bear in mind that there are no intermediate steps between the two keyframes that can be rendered in this case.
So the transition from 100% {content: '0';}
to 83.33333% {content: '5';}
completes after 10 seconds (as expected), but at some point between the property value changes. When set, animation-timing-function: linear;
this is exactly between two keyframes - after 5 seconds, this example gives the impression that 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 analyzed the behavior using the snippet above and looked at what happens if I remove individual keyframes (one at a time in the 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 the two given keyframes. The same can be seen in the following demo, where I changed 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 to solve the problem is simple, but not very pretty. You want to change a property in a CSS animation without animation and what the OP describes as a 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 will see that changing animation-timing-function: linear;
to something else, like animation-timing-function: ease-out;
, changes the point in time when the content changes abruptly. We noticed that the content changes exactly in the middle when using the linear sync feature, so I'm guessing it changes 50% of the progress between the two keyframes.
As permitted to define cubic-bezier
, we can shift this point in time when the progress reaches 50% towards the end.Note that your animation direction is reversed, so you need to flip 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>
source to share
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>
source to share