Welcome to WeChat public account: front-end detective
A good way to practice CSS is to draw all kinds of UI, such as a clock like this?
You can also visit this CSS clock to see it in action
There are several difficulties with CSS drawing such a layout:
- Scales arranged in a ring
- Circularly distributed numbers
- Autorun pointer
Let's implement it one by one, I believe you can learn a lot of CSS drawing and animation skills
1. Scales arranged in a ring
When it comes to "ring", conic gradient conic -gradient comes to mind. Suppose there is such a container
<clock></clock>
Add a little taper gradient
clock{
width: 300px;
height: 300px;
background: conic-gradient(#333 0 15deg, #cddc39 0deg 30deg);
}
can get this effect
How to make a staggered effect? You can try repeating-conic-gradient
clock{
/**/
background: repeating-conic-gradient(#333 0 15deg, #cddc39 0deg 30deg);
}
The effect is as follows
Still can't see anything to do with the scale? It doesn't matter, let's make the angle of the black part smaller
clock{
/**/
background: repeating-conic-gradient(#333 0 1deg, #cddc39 0deg 30deg);
}
The effect is as follows
Do the lines drawn in this way just correspond to the scale of the clock?
Then turn the entire shape into a ring, which can be achieved with MASK, as follows
clock{
/**/
border-radius: 50%;
-webkit-mask: radial-gradient(transparent 145px, red 0);
}
The effect is as follows
In fact, there is a small detail here. The black part is not centered and needs to be corrected (you can change the starting angle and specify from). Then, change this grass green to transparent, the complete code is as follows
clock{
/**/
background: repeating-conic-gradient(from -.5deg, #333 0 1deg, transparent 0deg 30deg);
border-radius: 50%;
-webkit-mask: radial-gradient(transparent 145px, red 0);
}
final effect
The same is true for the minute scale, because there are 60 scales in total, so the minimum angle is 6 degrees (360 / 60), which is implemented as follows
clock{
/**/
background: repeating-conic-gradient(#333 0 1deg, #cddc39 0deg 6deg);
}
Using the feature that CSS backgrounds can be infinitely superimposed, these two backgrounds can be drawn under the same element, so the complete code is as follows
clock{
/**/
background: repeating-conic-gradient(from -.5deg, #333 0 1deg, transparent 0deg 30deg),
repeating-conic-gradient(from -.5deg, #ccc 0 1deg, transparent 0deg 6deg);
border-radius: 50%;
-webkit-mask: radial-gradient(transparent 145px, red 0);
}
The final dial scale effect is as follows
2. Circular distribution of numbers
When I saw this layout, my first reaction was actually textPath . This SVG element allows text to be arranged along a specified path, such as the example on MDN below.
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<path id="MyPath" fill="none" stroke="red"
d="M10,90 Q90,90 90,45 Q90,10 50,10 Q10,10 10,40 Q10,70 45,70 Q70,70 75,50" />
<text>
<textPath href="#MyPath">
Quick brown fox jumps over the lazy dog.
</textPath>
</text>
</svg>
The effect is as follows
However, there is a flaw in this method, the angle of the text cannot be changed, it can only follow the vertical direction of the path, and the digital direction of the clock is normal.
After some pondering, I found that there is another way to have a similar layout along the path, that is offset-path ! Here is a demo effect on MDN
So what does it have to do with arranging numbers in a circle? Suppose there is such a layout
<clock-pane>
<num>1</num>
</clock-pane>
Then assign this number to a circular path (currently only paths are supported)
num{
offset-path: path('M250 125c0 69.036-55.964 125-125 125S0 194.036 0 125 55.964 0 125 0s125 55.964 125 125z');
}
The effect is as follows (the starting point follows the path path)
Then, the position of the element on the path can be changed by offset-distance , and percentages are supported
num{
offset-path: path('M250 125c0 69.036-55.964 125-125 125S0 194.036 0 125 55.964 0 125 0s125 55.964 125 125z');
offset-distance: 100%
}
Below is the change from 0 to 100%
By default, the angle of the element is also adaptively perpendicular to the path, similar to textPath . But we can manually specify a fixed angle, we need offset-rotate , just specify 0deg
num{
offset-path: path('M250 125c0 69.036-55.964 125-125 125S0 194.036 0 125 55.964 0 125 0s125 55.964 125 125z');
offset-rotate: 0deg;
offset-distance: 100%
}
The effect is as follows, the angle has been completely corrected
If you have similar layout requirements, can you refer to this case?
Next, we automatically reset the 12 numbers to the specified position through CSS variables
<clock-pane>
<num style="--i:1">1</num>
<num style="--i:2">2</num>
<num style="--i:3">3</num>
<num style="--i:4">4</num>
<num style="--i:5">5</num>
<num style="--i:6">6</num>
<num style="--i:7">7</num>
<num style="--i:8">8</num>
<num style="--i:9">9</num>
<num style="--i:10">10</num>
<num style="--i:11">11</num>
<num style="--i:12">12</num>
</clock-pane>
With calc calculation, the complete code is as follows
num{
position: absolute;
offset-path: path('M250 125c0 69.036-55.964 125-125 125S0 194.036 0 125 55.964 0 125 0s125 55.964 125 125z');
offset-distance: calc( var(--i) * 10% / 1.2 - 25%);
offset-rotate: 0deg;
}
The effect is as follows
3. The pointer of automatic operation
The drawing of the three pointers should not be too difficult, assuming the structure is as follows
<hour></hour>
<min></min>
<sec></sec>
Need to pay attention to the center of rotation
hour{
position: absolute;
width: 4px;
height: 60px;
background: #333;
transform-origin: center bottom;
transform: translateY(-50%) rotate(30deg);
}
min{
position: absolute;
width: 4px;
height: 90px;
background: #333;
transform-origin: center bottom;
transform: translateY(-50%) rotate(60deg);
}
sec{
position: absolute;
width: 2px;
height: 120px;
background: red;
transform-origin: center bottom;
transform: translateY(-50%) rotate(90deg);
}
sec::after{
content: '';
position: absolute;
width: 10px;
height: 10px;
border-radius: 50%;
left: 50%;
bottom: 0;
background: #fff;
border: 4px solid #333;
transform: translate(-50%, 50%);
}
The effect is as follows
At this point, the static layout is complete, so how does it work?
Did you still use timers earlier in this article? CSS can also implement electronic clocks . It is explained in detail that it can be achieved with CSS animations without the help of timers!
Back here, the principle of operation is very simple, it is an infinite loop CSS animation, as follows
@keyframes clock {
to {
transform: translateY(-50%) rotate(360deg);
}
}
The difference is that the cycles of the hour hand, minute hand and second hand are different. One rotation of the hour hand is 12 hours, the minute hand is 60 minutes, and the second hand is 60 seconds. Each needs to be converted into seconds (CSS units only support seconds and milliseconds ). For convenience Test, here the speed is increased by 60s → 6s
The code implementation is ( --step
is one minute)
hour{
/**/
transform: translateY(-50%) rotate(0);
animation: clock calc(var(--step) * 60 * 12) infinite;
}
min{
/**/
transform: translateY(-50%) rotate(0);
animation: clock calc(var(--step) * 60) infinite;
}
sec{
/**/
transform: translateY(-50%) rotate(0);
animation: clock var(--step) infinite;
}
The effect is as follows
Is it a little strange? When the second hand rotates, it first becomes faster, and then slowly becomes slower. This is because the default animation function is ease
, so it needs to be changed to linear
sec{
/**/
animation: clock var(--step) infinite linear;
}
So much better. However, the clocks and second hands that are usually seen usually walk and stop, and there is a sense of "tick-tick" rhythm, not this seamless. In CSS animation, is it a bit like a ladder? Yes, you can use the steps function of CSS. If you don’t understand this, you can refer to this article by Mr. Zhang: The steps function in the CSS3 animation property is introduced in depth , and the implementation is as follows
sec{
/**/
animation: clock var(--step) infinite steps(60);
}
The effect is as follows
Next you need to initialize the time via JS, that's all. It should be noted that when an offset is added, such as 12:30, the minute hand is actually 12.5, and so on. The code is implemented as follows
const d = new Date()
const h = d.getHours();
const m = d.getMinutes();
const s = d.getSeconds();
clock.style.setProperty('--ds', s)
clock.style.setProperty('--dm', m + s/60)
clock.style.setProperty('--dh', h + m/60 + s/3600)
Then in CSS, you can specify the starting position of the animation by animation-delay
hour{
/**/
animation: clock calc(var(--step) * 60 * 12) infinite linear;
animation-delay: calc( -1 * var(--step) * var(--dh) * 60);
}
min{
/**/
animation: clock calc(var(--step) * 60) infinite linear;
animation-delay: calc( -1 * var(--step) * var(--dm));
}
sec{
/**/
animation: clock var(--step) infinite steps(60);
animation-delay: calc( -1 * var(--step) * var(--ds) / 60 );
}
Then add some outline decoration to achieve the effect at the beginning of the article
The complete code can be viewed in CSS clock
Fourth, a brief summary
The above is the whole process of CSS drawing clock. This article focuses more on the drawing process, especially the circular layout skills. Here is a brief summary.
- When you encounter a ring pattern, you can think of conic-gradient
- The principle of circular scale drawing is conic-gradient and MASK clipping
- Text can be arranged along the path using textPath
- textPath does not support changing the text angle
- offset-path allows elements to be laid out along a specified path
- offset-path can set the offset and angle of the element on the path
- The principle of automatic clock running is CSS animation
- The difference between clock, minutes and seconds is the animation duration
- The "tick-tock" effect can be achieved with steps
- Time initialization can be achieved by animation delay
Of course, the focus of this article is not only on the realization of a clock, but on the accumulation and mastery of CSS drawing skills. With the relevant accumulation, when you encounter a similar layout in the future, filter it in your mind, and you can find a solution immediately. Finally, if you think it's good and helpful to you, please like, bookmark, and forward ❤❤❤
Welcome to WeChat public account: front-end detective