三步教你编写一个 Neumorphism 风格的小时钟

目录
序 Neumorphism
正文开始
一、时钟表盘编写
二、时钟指针编写
、时钟组件的调用
完整组件代码
序
又到了自己编写组件的时节了

作为一名前端开发者,平时也是阔以没事可以写写自定义的组件,增加一下自己的阅历,实际开发中用到也可以 通过 CV 大法去快速开发
先提前来看一下最终的效果:

先问一个小问题,现在大家现在更习惯于看哪种样式的时间,是钟表类型还是数字类型?(虽然你们的回答影响不了我做一个钟表啦)
钟表时间:

数字时间:

Neumorphism
目前网上的一些时钟风格不太喜欢,之前偶然看到一款新拟物风格Neumorphism的设计,让我眼前不禁一亮所以本次时钟组件的风格就决定是你啦---新拟物

Neumorphism 风格页面

<br>
正文开始
上面又水了一堆话,增加了一下文字的数量,真是太开森了
下面正式开始组件的制作流程
一、时钟表盘编写
因为本人使用的是 vue,所以编写的也是.vue 组件
先搭建一个clock.vue的组件
,设定好props
可能会接收的宽高,还有阴影颜色。阴影部分使用box-shadow
进行设置
其中像 Compute 中的 styleVar 内容类似于 CSS 中的:root 也可以去此文章中查看相关方法:自己动手丰衣足食系列の自定义下拉框 vue 组件
在主页面 app.vue 中设置全局颜色为#EBE6DA
在 CSS 样式编写中,计算位置等值可以使用 calc 加 var 的方式计算
代码:
<template>
<!-- 时钟 -->
<div class="clock" :style="styleVar">
<!-- 内圈 -->
<div class="innerBorder">
<!-- 时钟刻度 -->
<div class="scale mark1"></div>
<div class="scale mark2"></div>
<div class="scale mark3"></div>
<div class="scale mark4"></div>
<div class="scale mark5"></div>
<div class="scale mark6"></div>
<div class="scale mark7"></div>
<div class="scale mark8"></div>
</div>
<!-- 内圈波浪 -->
<div class="wave"></div>
</div>
</template>
<script>
export default {
name: 'clock',
components: {},
props: {
bgsize: {
type: Number,
default: 180
},
bgcolor: {
type: String,
default: '#D1CCC0'
}
},
data() {
return {
};
},
created() {},
mounted() {},
watch: {},
computed: {
styleVar() {
return {
'--clock-size': this.bgsize + 'px',
'--clock-bgcolor': this.bgcolor,
'--length-log2E': ((this.bgsize - 10) / 2) / Math.LOG2E + 'px',
}
}
},
methods: {
},
};
</script>
<style scoped>
/* 外部大小和阴影 */
.clock {
width: var(--clock-size);
height: var(--clock-size);
border-radius: 50%;
box-shadow: 5px 5px 10px var(--clock-bgcolor), -4px -4px 8px #fff;
}
/* 内阴影 */
.innerBorder {
/* calc加var计算 */
width: calc(var(--clock-size) - 10px);
height: calc(var(--clock-size) - 10px);
position: relative;
left: 5px;
top: 5px;
box-shadow: -3px -3px 6px #fff inset, 3px 3px 6px var(--clock-bgcolor) inset;
border-radius: 50%;
}
/* 时钟刻度 */
.scale {
width: 2px;
height: 8px;
position: absolute;
box-shadow: 1px 1px 1px #9D958F inset, -1px -1px 1px var(--clock-bgcolor) inset;
border-radius: 1px;
}
/* 设置小刻度位置 */
.mark1 {
left: calc(50% - 1px);
top: 2px;
}
.mark2 {
transform: rotate(45deg);
left: calc(50% + var(--length-log2E) );
top: calc(50% - var(--length-log2E) );
}
.mark3 {
transform: rotate(90deg);
left: calc(100% - 7px);
top: calc(50% - 1px);
}
.mark4 {
transform: rotate(135deg);
left: calc(50% + var(--length-log2E) - 5px);
top: calc(50% + var(--length-log2E) - 5px);
}
.mark5 {
left: calc(50% - 1px);
bottom: 2px;
}
.mark6 {
transform: rotate(45deg);
left: calc(50% - var(--length-log2E) + 5px);
top: calc(50% + var(--length-log2E) - 5px);
}
.mark7 {
transform: rotate(90deg);
left: 2px;
top: calc(50% - 1px);
}
.mark8 {
transform: rotate(135deg);
left: calc(50% - var(--length-log2E) );
top: calc(50% - var(--length-log2E) );
}
/* 中间波浪 */
.wave {
width: 30%;
height: 30%;
position: absolute;
left: 35%;
top: 35%;
box-shadow: 3px 3px 9px var(--clock-bgcolor), -3px -3px 9px #fff;
border-radius: 50%;
filter: blur(1px);
animation: wavemove 4s infinite linear;
}
@keyframes wavemove {
0% {
transform: scale(0.5);
}
50% {
transform: scale(1);
opacity: 1;
}
100% {
transform: scale(1.7);
opacity: 0;
}
}
</style>
效果:

二、时钟指针编写
在编写指针时需要注意,指针的旋转运动是围绕圆心进行的,使用 transform 来改变旋转
所以需要使用transform-origin: bottom
来设置选择的基准点,之后旋转rotate
改变

在样式编写完成之后,就可以将当前的北京时间与指针、分针、秒针的转动相结合起来了
这里的原理其实更加简单,使用new Date()
获取到当前的时分秒,然后再将时分秒按比例转换成度数deg
// 校准时钟指针方法
currentTime:function(){
let date = new Date();
this.hour = date.getHours();
this.minute = date.getMinutes();
this.second = date.getSeconds();
document.getElementsByClassName('hourhand')[0].style.transform = 'rotate(' + this.hour / 24 * 360 + 'deg)';
document.getElementsByClassName('minhand')[0].style.transform = 'rotate(' + this.minute / 60 * 360 + 'deg)';
document.getElementsByClassName('sechand')[0].style.transform = 'rotate(' + this.second / 60 * 360 + 'deg)';
}
使用 setInterval 定时器定时 1000 毫秒执行一次
这样就可以达到最终想要的效果了

三、时钟组件的调用
编写好时钟组件后,接下来就可以在页面上进行调用了
import clock from './components/clock'
export default {
name: 'App',
components: {
clock
}
}
调用<clock></clock>
就能够将时钟放置到你想要的位置因为之前设置了 props 的原因,所以可以通过简单的设置调整时钟的大小和颜色风格<clock :bgsize="150" :bgcolor=color></clock>
color为#bec8e4
现在的时钟样式:

完整组件代码
<template>
<!-- 时钟 -->
<div class="clock" :style="styleVar">
<!-- 内圈 -->
<div class="innerBorder">
<!-- 时钟刻度 -->
<div class="scale mark1"></div>
<div class="scale mark2"></div>
<div class="scale mark3"></div>
<div class="scale mark4"></div>
<div class="scale mark5"></div>
<div class="scale mark6"></div>
<div class="scale mark7"></div>
<div class="scale mark8"></div>
</div>
<!-- 内圈波浪 -->
<div class="wave"></div>
<!-- 时针、分针、秒针 -->
<div class="hands hourhand"></div>
<div class="hands minhand"></div>
<div class="hands sechand"></div>
</div>
</template>
<script>
export default {
name: 'clock',
components: {},
props: {
bgsize: {
type: Number,
default: 180
},
bgcolor: {
type: String,
default: '#D1CCC0'
}
},
data() {
return {
hour: 0, //小时
minute: 0, //分钟
second: 0, //秒
};
},
created() {},
mounted() {
this.currentTime();
// 定时校准
setInterval(this.currentTime, 1000);
},
watch: {},
computed: {
styleVar() {
return {
'--clock-size': this.bgsize + 'px',
'--clock-bgcolor': this.bgcolor,
'--length-log2E': ((this.bgsize - 10) / 2) / Math.LOG2E + 'px',
}
}
},
methods: {
// 校准时钟指针方法
currentTime:function(){
let date = new Date();
this.hour = date.getHours();
this.minute = date.getMinutes();
this.second = date.getSeconds();
document.getElementsByClassName('hourhand')[0].style.transform = 'rotate(' + this.hour / 24 * 360 + 'deg)';
document.getElementsByClassName('minhand')[0].style.transform = 'rotate(' + this.minute / 60 * 360 + 'deg)';
document.getElementsByClassName('sechand')[0].style.transform = 'rotate(' + this.second / 60 * 360 + 'deg)';
}
},
};
</script>
<style scoped>
/* 外部大小和阴影 */
.clock {
width: var(--clock-size);
height: var(--clock-size);
border-radius: 50%;
box-shadow: 5px 5px 10px var(--clock-bgcolor), -4px -4px 8px #fff;
}
/* 内阴影 */
.innerBorder {
/* calc加var计算 */
width: calc(var(--clock-size) - 10px);
height: calc(var(--clock-size) - 10px);
position: relative;
left: 5px;
top: 5px;
box-shadow: -3px -3px 6px #fff inset, 3px 3px 6px var(--clock-bgcolor) inset;
border-radius: 50%;
}
/* 时钟刻度 */
.scale {
width: 2px;
height: 8px;
position: absolute;
box-shadow: 1px 1px 1px #9D958F inset, -1px -1px 1px var(--clock-bgcolor) inset;
border-radius: 1px;
}
/* 设置小刻度位置 */
.mark1 {
left: calc(50% - 1px);
top: 2px;
}
.mark2 {
transform: rotate(45deg);
left: calc(50% + var(--length-log2E) );
top: calc(50% - var(--length-log2E) );
}
.mark3 {
transform: rotate(90deg);
left: calc(100% - 7px);
top: calc(50% - 3px);
}
.mark4 {
transform: rotate(135deg);
left: calc(50% + var(--length-log2E) - 5px);
top: calc(50% + var(--length-log2E) - 5px);
}
.mark5 {
left: calc(50% - 1px);
bottom: 2px;
}
.mark6 {
transform: rotate(45deg);
left: calc(50% - var(--length-log2E) + 5px);
top: calc(50% + var(--length-log2E) - 5px);
}
.mark7 {
transform: rotate(90deg);
left: 2px;
top: calc(50% - 3px);
}
.mark8 {
transform: rotate(135deg);
left: calc(50% - var(--length-log2E) );
top: calc(50% - var(--length-log2E) );
}
/* 中间波浪 */
.wave {
width: 30%;
height: 30%;
position: absolute;
left: 35%;
top: 35%;
box-shadow: 3px 3px 9px var(--clock-bgcolor), -3px -3px 9px #fff;
border-radius: 50%;
filter: blur(1px); /* 模糊处理 */
animation: wavemove 4s infinite linear;
}
@keyframes wavemove {
0% {
transform: scale(0.5);
}
50% {
transform: scale(1);
opacity: 1;
}
100% {
transform: scale(1.7);
opacity: 0;
}
}
/*************************** 时针、分针、秒针 ****************************/
.hands {
position: absolute;
transform-origin: bottom;
border-radius: 10px;
z-index: 300;
bottom: 50%;
left: calc(50% - 1px);
}
.sechand {
width: 2px;
height: 42%;
background-image: linear-gradient( #CD69C9, #1CA6F3);
}
.minhand {
width: 4px;
height: 33%;
background-color: #9BA5AA;
left: calc(50% - 2px);
}
.hourhand {
width: 6px;
height: 21%;
background-color: #BEC8E4;
left: calc(50% - 3px);
}
</style>
版权声明: 本文为 InfoQ 作者【空城机】的原创文章。
原文链接:【http://xie.infoq.cn/article/a46829a315e8967acdac8023d】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。

空城机
曾经沧海难为水,只是当时已惘然 2021.03.22 加入
业余作者,在线水文 主要干前端的活,业余会学学python 欢迎各位关注,互相学习,互相进步
评论