Welcome to pay attention to my public number: front-end detective

Usually want to do a clock, certainly cannot leave JS timer. Today we use CSS to implement a clock as follows:

You can also access the CSS time (codepen.io) to see it in action

Of course, it borrows a little JS for initialization time, and the entire clock is run by CSS. There are a lot of tricks that you may not know about, take a look

First, the transformation of numbers

Let’s see how the numbers change.

Previously, if you wanted to implement incremental changes in numbers, you might have to prepare the numbers in advance, such as this

<span>
	<i>1</i>
  <i>2</i>.<i>59</i>
</span>
Copy the code

And then you do that by changing the displacement.

However, there is now a simpler way to do this, and that is CSS @ Property. If you don’t understand this, please refer to this article: CSS @ Property. Make the impossible possible. What does this do? Basically, you can customize the properties, and in this case, you can make the numbers transition and animate like colors, so if you don’t know, just look at the example

Suppose your HTML looks like this

<span style="--num: 0"></span>
Copy the code

How to display CSS var values using the content property

span::after{
  counter-reset: num var(--num);
  content: counter(num);
}
Copy the code

You can then change this number with :hover

span:hover::after{
  --num: 59
}
Copy the code

It’s going from 0 to 59, which is pretty routine. If you use CSS property, the situation is different, with very little modification. Define –h first, and then give the variable a transition time, as follows

@property --h { 
  syntax: '<integer>';
  inherits: false;
  initial-value: 0;
}
span::after{
  transition: 1s --num;
}
Copy the code

An amazing thing happened

Doesn’t that seem crazy? With the @property definition, the variable itself can be transitioned independently, rather than depending on some transition-only property (color, width, etc.). You can even add animations using the Steps method and set the animation period to infinite, as shown below

@keyframes num {
  to {
    --num: 10}}span{
  animation: num 1s infinite steps(10);
}
Copy the code

This is the basic working principle of the clock, an infinite loop of CSS animation!

Two, time, minute, second

Let’s look at the specific implementation of hours, minutes, and seconds, with the following HTML

<div class="time">
  <span class="hour"></span>
  <a class="split">:</a>
  <span class="minitus"></span>
  <a class="split">:</a>
  <span class="seconds"></span>
</div>
Copy the code

Attach initial values for hours, minutes and seconds

@property --h { 
  syntax: '<integer>';
  inherits: false;
  initial-value: 0;
}
@property --m { 
  syntax: '<integer>';
  inherits: false;
  initial-value: 0;
}
@property --s { 
  syntax: '<integer>';
  inherits: false;
  initial-value: 0;
}
.hour::after{
  counter-reset: hour var(--h);
  content: counter(hour);
}
.minitus::after{
  counter-reset: minitus var(--m);
  content: counter(minitus);
}
.seconds::after{
  counter-reset: seconds var(--s);
  content: counter(seconds);
}
Copy the code

There is no linkage between hours, minutes, and seconds, so each requires a separate animation. Below we need to think about 🤔, if using CSS animation to achieve, each animation starting point and length is how much?

The hour hand is 0-23 and lasts for 24h, the minute hand is 0-59 and lasts for 60min, and the second hand is 0-59 and lasts for 60s. However, the CSS only supports seconds (s) or milliseconds (ms), so it needs to be converted to 60s*60*24, 60s*60, and 60s respectively. The concrete implementation is as follows:

@keyframes hour {
  to {
    --h: 24}}@keyframes minitus {
  to {
    --m: 60}}@keyframes seconds {
  to {
    --s: 60}}.hour::after{
  counter-reset: hour var(--h);
  content: counter(hour);
  animation: hour calc(60s * 60 * 24) infinite steps(24);
}
.minitus::after{
  counter-reset: minitus var(--m);
  content: counter(minitus);
  animation: minitus calc(60s * 60) infinite steps(60);
}
.seconds::after{
  counter-reset: seconds var(--s);
  content: counter(seconds);
  animation: seconds 60s infinite steps(60);
}
Copy the code

For the sake of observation, the time is set 10 times faster (60s => 6s) as follows

Three, time, minute, second automatic zero

There is a problem with the layout above. The change in the width of the 1 and 2 bits causes the clock to “wobble”, so a “0” needs to be added at the 1 bit. About CSS zeroing, previously in this article: Can CSS also auto-complete strings? Since counters are used here, the option to change the counter style is directly selected by decimal-leading-Zero as follows

.hour::after{
  / * * /
  content: counter(hour, decimal-leading-zero);/* Add counter style */
}
Copy the code

It’s much more harmonious

4. Time initialization

It all started at 00:00:00, so you need to manually specify the initial time. So let’s say it’s 19:26:30, how do we initialize it?

In order to facilitate control, three variables — DH, –dm and — DS are used to represent the initial time. Note that since animation-delay only supports seconds (s) or milliseconds (ms), it also needs to be converted, as follows

:root{
  --dh: 19;
  --dm: 26;
  --ds: 30;
}
.hour::after{
  / * * /
  animation: hour calc(60s * 60 * 24) infinite steps(24);
  animation-delay: calc(-60s * 60 * var(--dh) );
}
.minitus::after{
  / * * /
  animation: minitus calc(60s * 60) infinite steps(60);
  animation-delay: calc(-60s * var(--dm) );
}
.seconds::after{
  / * * /
  animation: seconds 60s infinite steps(60);
  animation-delay: calc(-1s * var(--ds) );
}
Copy the code

Isn’t that weird? The minutes changed when the seconds reached 30, half a minute late. The reason for this is that, although numerically the minutes are 26, we also need to take into account the movement of the seconds. For example, in this case, the minutes are actually half gone and should be 26.5 (26 + 30/60), so we need to add the offset to the calculation. Let’s get the real time from JS and fix the offset

const d = new Date(a)const h = d.getHours();
const m = d.getMinutes();
const s = d.getSeconds();
document.body.style.setProperty('--ds', s)
document.body.style.setProperty('--dm', m + s/60)
document.body.style.setProperty('--dh', h + m/60 + s/3600)
Copy the code

That’s normal

5. Flashing delimiters

To make the clock look more “dynamic”, add a flicker animation to the delimiter as follows

@keyframes shark {
  0%.100%{
    opacity: 1;
  }
  50%{
    opacity: 0; }}.split{
  animation: shark 1s step-end infinite;
}
Copy the code

Now let’s look at the final result

Full code access CSS time (codepen.io)

Six, to sum up

I can’t imagine how much CSS knowledge and skills went into implementing a clock effect, so let’s summarize

  1. The CSS implementation is essentially an infinite loop of CSS animations
  2. Flexible use of CSS CALC computing
  3. CSS counters can display CSS variables on a page via content
  4. Numeric changes can now be implemented using CSS @Property with animations
  5. The difference between time and seconds lies in the different animation duration and starting point of animation
  6. The CSS auto-zero can be seen in the previous article, where decimal-leading-Zero is used
  7. Time initialization is simply specifying the animation delay value
  8. When specifying the initial value, you also need to take into account the respective offset, such as 19:30:30, when the hour hand is actually 30.5
  9. Flicker animation of delimiter

In fact, the whole implementation process is a process of continuous thinking and learning. For example, in order to realize the change of numbers, we must learn the @property correlation, and in order to realize the zero filling, we need to understand the deeper level of counter correlation, as well as the various animations used. Finally, if you think it’s good and helpful, please like, bookmark and retweet ❤❤❤

Welcome to pay attention to my public number: front-end detective