随手敲了个时钟的demo

海星吧 2022-4-9 6981

先给大家看看时钟的效果

当然我的UI不咋地,随便看看就好了。

大家可能以为我写在这个demo的代码肯定很多,但是我可以负责任的告诉你们

js代码其实少的可怜,这个时钟demo我只需要获取到三个数值

hour小时,minute分钟,second秒

并且定时每秒获取一次就可以了。

那么我是怎么做到的呢?

BaseClock -->
<script setup>
import { useClock } from "@/hooks/useClock";
const { hour, minute, second } = useClock();
</script>
hooks/useClock.js --> 
import { ref, computed } from 'vue';
import { useSafeInterval } from '@/hooks/useSafeListener';

export function useClock() {
	const newDate = ref(new Date());
	const hour = computed(() => newDate.value.getHours());
	const minute = computed(() => newDate.value.getMinutes());
	const second = computed(() => newDate.value.getSeconds());
	function setTimestamp() {
		newDate.value = new Date()
	}
	useSafeInterval(() => {
		setTimestamp();
	});
	return {
		hour,
		minute,
		second,
	}
}

可以看到我用到了之前写的 useSafeInterval 这个自定义hook,

能够让我安全的使用定时器而不用担心产生副作用。

我把当前时间储存在newDate这个变量中,并且定时获取他,让他保持在当前时间点上。

然后再用计算属性储存他的三个值就好了。

          <div class="clock" :class="[`clock-${theme}`]">
			<ul class="scales">
				<li
					v-for="(item, index) in 12"
					:key="index"
					:style="`--scale:${(index * 360) / 12}deg`"
				>
					<span
						class="scale-text"
						:style="`--scale:${-((index * 360) / 12)}deg`"
						>{{ index }}</span
					>
				</li>
			</ul>
			<div class="hour" :style="`--scale:${hour * 30 + (minute / 2)}deg`"></div>
            <div class="minute" :style="`--scale:${minute * 6 + (second / 10)}deg`"></div>
            <div class="second" :style="`--scale:${second * 6}deg`"></div>
			<div class="dot"></div>
		</div>

这部分是定时器的html

clock是整个定时器的容器部分

ul.scales > li 是每个小时的刻度

在没有做定位之前是这样的

它们就是一个正常的文档流

我是如何做它们的定位的呢

这得使用position做定位,然后用transform旋转它们。

首先定位它们一下吧。

我的思路是先将它们的位置居中,然后顶部

.scales {
		position: relative;
        height: 100%;
		li {
			position: absolute;
			top: 0;
			left: 50%;
			height: 100%;
			transform: translateX(-50%);
		}
	}

然后可以看到它们都被重叠在一起了,高度呢也是整个时钟的100%。

那么这个时候如何旋转它们呢,在li标签里我设置了一个css变量

:style="`--scale:${(index * 360) / 12}deg`"

它的数值是v-for中的index * 360 / 12,我文中这样写是为了代码更加清楚它的数值,

其实写一个index * 30 也是一样的(360 / 12 = 30)

然后用rotateZ(var(--scale))为它们每一个做旋转,

.scales {
		position: relative;
		height: 100%;
		li {
			position: absolute;
			top: 0;
			left: 50%;
			height: 100%;
			transform: translateX(-50%) rotateZ(var(--scale));
			font-size: 12px;
			padding-top: 4px;
		}
	}

但是这样看起来不太对劲,我得把数字掰正才好,当然只需要将li旋转的数值反过来就行了。

<span class="scale-text" :style="`--scale:${-((index * 360) / 12)}deg`" >{{ index }}</span >

.scales {
		position: relative;
		height: 100%;
		li {
			position: absolute;
			top: 0;
			left: 50%;
			height: 100%;
			transform: translateX(-50%) rotateZ(var(--scale));
			font-size: 12px;
			padding-top: 4px;
			.scale-text {
				display: block;
				transform: rotateZ(var(--scale));
			}
		}
	}

那么接下来的指针就好办了。

<div class="hour" :style="`--scale:${hour * 30}deg`"></div>
<div class="minute" :style="`--scale:${minute * 6}deg`"></div>
<div class="second" :style="`--scale:${second * 6}deg`"></div>
<div class="dot"></div>

分针和秒针需要进行1/60的刻度,而时针进行的是1/12的刻度。

360 / 60 = 6

360 / 12 = 30

照着上面的刻度写一遍样式

    .hour,
	.minute,
	.second {
		border-radius: 6px;
		position: absolute;
		top: 50%;
		left: 50%;
		transform-origin: bottom center;
		transform: translate(-50%, -100%) rotateZ(var(--scale));
	}
	.hour {
		height: 20%;
		width: 3px;
	}
	.minute {
		height: 26%;
		width: 2px;
	}
	.second {
		height: 38%;
		width: 1px;
	}

当然这个并不是最正确的答案,真正的时钟,时针和分钟的度数并不会保持在一个地方太久,它都是证据分钟的刻度变化的

<p>{{hour * 30 + (minute / 2)}}</p>
<p>{{minute * 6 + (second / 10) }}</p>
<p>{{second * 6}}</p>

 

分针在时钟上,每一刻度为 360 / 60 = 6

秒钟每一秒增加分针的度数为 6/60 = 0.1

所以每一秒增加0.1的度数,让分针每秒加上0.1也就是秒数除以10就行了。

时针每一刻为 360/12=30

每分钟在时针上增加 30/60  = 0.5

所以让时针加上分针/2的数值就行了

当然至于秒钟在分针上增加的刻度我算了一下,大约每秒0.008333度,实在没法看,所以不加了。

有兴趣的朋友可以加一下。

上面有部分是没有说的,不过关系不大,就那个每小时旁边的杠杠

<ul class="scales scales-BA">
	<li v-for="(item, index) in 60" :key="index" :style="`--scale:${index * 6}deg`"
		v-show="index % 5 != 0">
		<div class="scale"></div>
	</li>
</ul>

.scales-BA {
	top: -100%;
	.scale {
		height: 4px;
		width: 1px;
	}
}

整一个刻度的复制,数量为60也就是每分钟,然后余5为0不显示就行了。

当然top-100%是为了对齐时钟的内容,不然是溢出的。

 

以下是所有的代码

BaseClock.vue
<template>
	<div class="base-clock">
		<div>
			<label for="dark">
				<input type="radio" name="theme" id="dark" value="dark" v-model="theme" />dark
			</label>
			<br />
			<label for="light">
				<input type="radio" name="theme" id="light" value="light" v-model="theme" />light
			</label>
		</div>
		<div class="clock" :class="[`clock-${theme}`]">
			<ul class="scales">
				<li v-for="(item, index) in 12" :key="index"
					:style="`--scale:${(index * 360) / 12}deg`">
					<span class="scale-text" :style="`--scale:${-((index * 360) / 12)}deg`">{{ index }}</span>
				</li>
			</ul>
			<ul class="scales scales-BA">
				<li v-for="(item, index) in 60" :key="index" :style="`--scale:${index * 6}deg`"
					v-show="index % 5 != 0">
					<div class="scale"></div>
				</li>
			</ul>
			<div class="hour" :style="`--scale:${hour * 30 + (minute / 2)}deg`"></div>
			<div class="minute" :style="`--scale:${minute * 6 + (second / 10)}deg`"></div>
			<div class="second" :style="`--scale:${second * 6}deg`"></div>
			<div class="dot"></div>
		</div>
	</div>
</template>

<script setup>
import { ref, defineProps, toRefs } from "vue";
import { useClock } from "@/hooks/useClock";
const props = defineProps({
	size: {
		type: Number,
		default: 180,
	},
	themeType: {
		type: String,
		default: "light",
		vaildator: (value) => ["dark", "light"].includes(value),
	},
});
const { size } = toRefs(props);
const theme = ref(props.themeType);
const clockSize = `${size.value}px`;
const { hour, minute, second } = useClock();
</script>

<style lang="scss" scoped>
.clock {
	width: v-bind("clockSize");
	height: v-bind("clockSize");
	border-radius: 50%;
	position: relative;
	&-dark {
		background-color: rgba($color: #000000, $alpha: 0.8);
		.scales li {
			color: #fff;
		}
		.hour,
		.minute,
		.second,
		.scales-BA .scale {
			background-color: rgb(255, 255, 255);
		}
	}
	&-light {
		background-color: #ffffff;
		box-shadow: inset 0 0 2px 0 rgba($color: #000000, $alpha: 1);
		.scales li {
			color: inherit;
		}
		.hour,
		.minute,
		.second,
		.scales-BA .scale {
			background-color: #000;
		}
	}
	.dot {
		position: absolute;
		top: 50%;
		left: 50%;
		transform: translate(-50%, -50%);
		width: 4px;
		height: 4px;
		border-radius: 50%;
		background-color: red;
	}
	.scales {
		position: relative;
		height: 100%;
		li {
			position: absolute;
			top: 0;
			left: 50%;
			height: 100%;
			transform: translateX(-50%) rotateZ(var(--scale));
			font-size: 12px;
			padding-top: 4px;
			.scale-text {
				display: block;
				transform: rotateZ(var(--scale));
				position: relative;
			}
		}
	}
	.scales-BA {
		top: -100%;
		.scale {
			height: 4px;
			width: 1px;
		}
	}

	.hour,
	.minute,
	.second {
		border-radius: 6px;
		position: absolute;
		top: 50%;
		left: 50%;
		transform-origin: bottom center;
		transform: translate(-50%, -100%) rotateZ(var(--scale));
	}
	.hour {
		height: 20%;
		width: 3px;
	}
	.minute {
		height: 26%;
		width: 2px;
	}
	.second {
		height: 38%;
		width: 1px;
	}
}
</style>

hooks/useClock.js
import { ref, computed } from 'vue';
import { useSafeInterval } from '@/hooks/useSafeListener';

export function useClock() {
	const newDate = ref(new Date());
	const hour = computed(() => newDate.value.getHours());
	const minute = computed(() => newDate.value.getMinutes());
	const second = computed(() => newDate.value.getSeconds());
	function setTimestamp() {
		newDate.value = new Date()
	}
	useSafeInterval(() => {
		setTimestamp();
	});
	return {
		hour,
		minute,
		second,
	}
}

 

安全定时器 useSafeInterval

可以在我之前的帖子里找到。当然你也可以自己写一个,主要是在组件销毁前自动销毁定时器就行了。
 
我写这个时钟的时候还顺便弄了两个主题,
感觉还可以。
不喜欢的也可以去掉,保留一个或者自己写。
好了,这篇就到这里,感谢各位收看。
这里是海星吧,我们下次见,拜拜
最后于 2022-4-9 被海星吧编辑 ,原因: 完成帖子。
弱鸡程序员年底还在加班
最新回复 (3)
  • 海星吧 2022-4-9
    0 2
    欧派兽 奖励三级精华
    感谢感谢。
    弱鸡程序员年底还在加班
  • 海星吧 2022-4-9
    0 3
    喀秋莎 我看成了了homo
    homo是什么。。。
    弱鸡程序员年底还在加班
  • 海星吧 2022-4-9
    0 4
    Joy 技术佬强无敌
    还好,这东西算简单的,也就是活用了一下自定义hook的便捷和计算属性的响应式跟踪数据,
    你是没见过那些正真好看的时钟Demo,UI写的特别舒适。
    还有用canvas,threejs写的。
    以前看的gsap,外网的一个特别厉害的动画插件,但是文档英文,也没什么技术讲解的帖子看,
    那东西特别吃力,有时间搞一个这东西的帖子。(挖个坑,不一定填。)
    弱鸡程序员年底还在加班
    • ACG里世界
      5
          
返回
发新帖