This article started in typical fashion for me: that is, as another topic entirely. I plan to write an article on animated CSS3 media query transitions, but I was sidetracked by the question of how to display a changing browser width using CSS alone. Thus, this CSS3 odometer; for the purpose of this article, I’ve turned it into a timer.
Ultimately the technique shown here could be put to a lot of uses. First, an explanation of the basic markup:
The display needs three “wheels” of numbers for each segment of the timer, so a series of HTML lists makes eminent sense. As you will see, all of the lists will move to the beat of the same CSS animation routine. They’re also all wrapped in a <div>
with an id
of timer
.
You have been on this page for
<div id="timer">
<ul id="timer-tens">
<li>8
<li>9
<li>0
<li>1
<li>2
<li>3
<li>4
<li>5
<li>6
<li>7
<li>8
<li>9
</ul>
<ul id="timer-ones">
<li>9
<li>0
<li>1
<li>2
<li>3
<li>4
<li>5
<li>6
<li>7
<li>8
<li>9
<li>0
</ul>
</div> seconds
Note that the numerals above and below the digit shown in the center of the wheel at any moment are included.
(I would have dearly loved to use a proper ordered list to display the numbers, but the fact that most browsers do not yet support the HTML5 start
attribute for ordered lists, and my desire to show a glimpse of the broader counter, made that impractical. I’ve not shown the code for the hundreds-of-seconds counter to save space).
Then the CSS. Obviously, I need to restrict the visible area of each list. That will be the job of the containing div
:
div#timer {
display: inline-block;
height: 3.6rem;
overflow: hidden;
}
I’ve used inline-block
so the <div>
will still fall in line with the surrounding text, but still be provided with a height
. Let’s look at the CSS for the counters:
div#timer ul {
display: inline-block;
list-style-type: none;
width: 2rem;
padding-left: 0;
margin-top: 0;
position: relative;
top: -1.3rem;
line-height: 32px;
}
This is pretty simple. The line-height
specified here will interact with the general rules for the <body>
:
body {
background: #000;
color: #fff;
text-align: center;
font-size: 32px;
}
The two values guarantee that every digit in both lists is separated by the same amount of vertical space. It’s this until that we use to move the counters in the animation:
@keyframes rollover {
0% { top: -51px; }
10% { top: -83px; }
20% { top: -115px; }
30% { top: -147px; }
40% { top: -179px; }
50% { top: -211px; }
60% { top: -243px; }
70% { top: -275px; }
80% { top: -307px; }
90% { top: -339px; }
100% { top: -371px; }
}
The spacing of the numerals means that each animation sequence will start and end on the same digit, and that every numeral will always be displayed in the center of its wheel.
Now the really neat part: the animation of the tumblers. I wanted the movement between each numeral to be a very deliberate “tick”, so I created a custom easing curve and applied it to every step of the animation:
ul#timer-ones {
animation: rollover 10s cubic-bezier(1.000, 0.005, 0.995, 0.090) infinite;
}
The cool part is the fact that we apply the exact same animation sequence to the tens-of-seconds counter, as it moves in exactly the same way, just over a greater amount of time:
ul#timer-tens {
animation: rollover 100s cubic-bezier(1.000, 0.005, 0.995, 0.090) infinite;
}
That’s essentially it, with the addition of a gradient for the <div>
and alpha-masked, absolutely positioned PNG images inside to “shadow” the tumblers.
(For this article I haven’t turned on the animation for the hundreds-of-seconds counter, as I don’t expect anyone to stick around on the page for that long… that said, it will be very interesting to look at my Google Analytics numbers later this week to see if the time-on-page metric has gone up due to visitors being obsessed, Hook-like, with a ticking clock).
Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.