57
头图
Welcome to my public account: front-end detective

Usually to make a clock, it must be inseparable from the JS timer. Another way of thinking today is to use CSS to implement a clock, as follows:

Kapture 2022-03-04 at 16.28.36

You can also visit this CSS time (codepen.io) to see it in action

Of course, a little bit of JS is used to initialize the time. The entire clock is run by CSS. There are many tricks you may not know about. Let's take a look.

The transformation of numbers

Let's first look at how the numbers are transformed.

In the past, if you wanted to implement incremental changes in numbers, you might need to prepare those numbers in advance, for example like this

 <span>
    <i>1</i>
  <i>2</i>
  ...
  <i>59</i>
</span>

Then do it by changing the displacement.

However, now there is a more concise way to achieve it, that is CSS @property , you can refer to this article if you don’t understand this: CSS @property makes the impossible possible . What is this for? To put it simply, you can customize the properties. In this example, you can make the numbers transition and animate like colors . You may not understand much. Just look at the example.

Suppose the HTML is like this

 <span style="--num: 0"></span>

We let this custom variable be displayed on the page. Simple content cannot display the custom variable directly, you need to use a timer. If you are interested, you can refer to this article: Tips: How to display with the content attribute CSS var variable value

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

image-20220304165629730

Then, this number can be changed by :hover

 span:hover::after{
  --num: 59
}

Kapture 2022-03-04 at 17.09.17

Very bluntly changed from 0 to 59, which is very conventional. If you use CSS property, the situation is different. There are very few places to be modified. First define --h , and then give this variable a transition time, as follows

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

A miraculous scene happened

Kapture 2022-03-04 at 17.14.07

It looks incredible? It can be understood in this way that after being defined by @property , this variable itself can be set to transition independently, instead of depending on some properties that only support transition ( color , width etc). You can even add animation, you need to use the steps method to set the animation period to infinite, as follows

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

The basic working principle of the clock is just that, an infinite loop CSS animation!

Kapture 2022-03-04 at 17.26.23

2. Hours, minutes, seconds

Let's look at the implementation of the specific hours, minutes and seconds. The HTML is as follows

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

Attach initial values to 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);
}

image-20220304183029959

There is no linkage between hours, minutes, and seconds here, so each requires a separate animation. Next, you need to think about it🤔, if you use CSS animation to achieve, what is the starting point and duration of each animation?

Yes, that is what you think, the clock is 0-23 , long 24h , the minute hand is 0-59 , long 60min , second hand 0-59 --- 0-59 , long 60s , but only time unit in CSS support 秒(s) or 毫秒(ms) , so there need to convert it, are long 60s*60*24 , 60s*60 , 60s , the specific 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);
}

Here, for the convenience of observation, the time is adjusted 10 times faster (60s => 6s), as follows

Kapture 2022-03-04 at 18.57.48

Three, hour, minute, second automatic zero filling

There is a problem with the above layout. The change in the width of 1-digit and 2-digit causes the clock to "shake" as a whole, so it is necessary to add a "0" to the 1-digit. About CSS zero-padding, before this article: Can CSS also auto-complete strings? Three schemes are mentioned in the text. Since the counter is used here, the method of changing the counter style is directly selected, which is realized by decimal-leading-zero . The specific method is as follows

 .hour::after{
  /**/
  content: counter(hour, decimal-leading-zero);/*添加计数器样式*/
}

It's more harmonious

Kapture 2022-03-04 at 19.04.13

4. Time initialization

It all started from 00:00:00 , so you need to manually specify the initial time. Suppose it is now 19:26:30 , how to initialize it?

It should be used animation-delay to move to the next location specified in advance, in order to facilitate control, using three variables --dh , --dm , --ds to Indicates the initial time. Note that since animation-delay also only supports 秒(s) or 毫秒(ms) , it also needs to be converted, the implementation is 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) );
}

Kapture 2022-03-04 at 19.36.13

Is it a little strange? The minute changes when the second reaches 30, half a minute later. The reason is this, although numerically, the minute is 26, but also consider the movement of the second, for example, in this case, the minute has actually gone half way, it should be 26.5 (26 + 30 / 60), Therefore, the offset needs to be added in the calculation. Below we get the real time through JS and fix the offset

 const d = new Date()
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)

That's normal

Kapture 2022-03-04 at 19.45.04

5. Blinking Separator

In order to make the clock look more "dynamic", you can add a blinking animation to the separator, the code is as follows

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

Now look at the final effect

Kapture 2022-03-04 at 19.49.03

The full code can be accessed at CSS time (codepen.io)

6. To summarize

Unexpectedly to achieve a clock effect, using so much CSS knowledge and skills, briefly summarize it

  1. CSS implementation is essentially infinite loop CSS animation
  2. Flexible use of CSS calc calculations
  3. CSS counter can display CSS variables on the page through content
  4. Number changes can now be animated with CSS @property
  5. The difference between hours, minutes and seconds is that the animation duration and animation starting point are different.
  6. CSS automatic zero filling can refer to the previous article, here is implemented with decimal-leading-zero
  7. Time initialization is actually specifying the animation delay value
  8. When specifying the initial value, you also need to take into account the respective offsets, such as 19:30:30, the hour hand number at this time is actually 30.5
  9. Blink animation of separator

In fact, the entire implementation process is a process of continuous thinking and learning. For example, in order to realize the change of numbers, you must learn about @property. In order to realize zero-filling, you need to understand deeper counter-related and various animations used. . Finally, if you think it's good and helpful to you, please like, bookmark, and forward ❤❤❤

Welcome to my public account: front-end detective

XboxYan
18.1k 声望14.1k 粉丝