update at 2026-03-17 10:37:27

This commit is contained in:
douboer@gmail.com
2026-03-17 10:37:27 +08:00
parent e5becf63cf
commit 192eb1b8d1
44 changed files with 5208 additions and 403 deletions

View File

@@ -0,0 +1,164 @@
<script setup lang="ts">
import { computed } from 'vue';
import { buildClockState } from '@/lib/clock';
import type { DashboardMode } from '@/lib/dashboard-mode';
const SIMPLE_CLOCK_SIZE = 480;
// 5 分钟刻度的外沿需要和普通刻度落在同一圈上,
// 所以这里按统一 outer inset 重新计算大刻度中心距离。
const SIMPLE_HOUR_TICK_DISTANCE = 194.59034156799316;
const SIMPLE_MINUTE_TICK_DISTANCE = 207.39662265777588;
const SIMPLE_HOUR_HAND_FRONT = 148.8;
const SIMPLE_HOUR_HAND_BACK = 67.2;
// 分针前端要求和分钟刻度外端严格对齐。
const SIMPLE_MINUTE_HAND_FRONT = 218.26450538635254;
const SIMPLE_MINUTE_HAND_BACK = 72;
const SIMPLE_MINUTE_HAND_THICKNESS = 9.6;
const props = withDefaults(
defineProps<{
date: Date;
mode: DashboardMode;
size?: number;
}>(),
{
size: SIMPLE_CLOCK_SIZE,
},
);
const clockState = computed(() => buildClockState(props.date));
const scale = computed(() => props.size / SIMPLE_CLOCK_SIZE);
// simple 主题的表盘不是图片,而是按 Figma 参数直接绘制。
const hourTicks = Array.from({ length: 12 }, (_, index) => index * 30);
const minuteTicks = Array.from({ length: 60 }, (_, index) => index).filter((index) => index % 5 !== 0);
const stageStyle = computed(() => ({
width: `${props.size}px`,
height: `${props.size}px`,
}));
function buildHourTickStyle(angle: number) {
return {
width: `${14.4 * scale.value}px`,
height: `${47.34832763671875 * scale.value}px`,
transform: `translate(-50%, -50%) rotate(${angle}deg) translateY(-${SIMPLE_HOUR_TICK_DISTANCE * scale.value}px)`,
};
}
function buildMinuteTickStyle(index: number) {
return {
width: `${6.520815372467041 * scale.value}px`,
height: `${21.73549461364746 * scale.value}px`,
transform: `translate(-50%, -50%) rotate(${index * 6}deg) translateY(-${SIMPLE_MINUTE_TICK_DISTANCE * scale.value}px)`,
};
}
const hourHandStyle = computed(() => ({
width: `${14.4 * scale.value}px`,
height: `${(SIMPLE_HOUR_HAND_FRONT + SIMPLE_HOUR_HAND_BACK) * scale.value}px`,
transformOrigin: `50% ${SIMPLE_HOUR_HAND_BACK * scale.value}px`,
transform: `translate(-50%, -${SIMPLE_HOUR_HAND_BACK * scale.value}px) rotate(${clockState.value.hourAngle}deg)`,
}));
const minuteHandStyle = computed(() => ({
width: `${SIMPLE_MINUTE_HAND_THICKNESS * scale.value}px`,
height: `${(SIMPLE_MINUTE_HAND_FRONT + SIMPLE_MINUTE_HAND_BACK) * scale.value}px`,
transformOrigin: `50% ${SIMPLE_MINUTE_HAND_BACK * scale.value}px`,
transform: `translate(-50%, -${SIMPLE_MINUTE_HAND_BACK * scale.value}px) rotate(${clockState.value.minuteAngle}deg)`,
}));
</script>
<template>
<div class="simple-analog-clock" :style="stageStyle" data-clock-region="true">
<template v-if="mode !== 'background'">
<div class="simple-analog-clock__face-shadow" />
<div class="simple-analog-clock__face" />
<div
v-for="angle in hourTicks"
:key="`hour-${angle}`"
class="simple-analog-clock__tick simple-analog-clock__tick--hour"
:style="buildHourTickStyle(angle)"
/>
<div
v-for="index in minuteTicks"
:key="`minute-${index}`"
class="simple-analog-clock__tick simple-analog-clock__tick--minute"
:style="buildMinuteTickStyle(index)"
/>
<div class="simple-analog-clock__hand simple-analog-clock__hand--minute" :style="minuteHandStyle" />
<div class="simple-analog-clock__hand simple-analog-clock__hand--hour" :style="hourHandStyle" />
<div class="simple-analog-clock__center simple-analog-clock__center--bottom" />
<div class="simple-analog-clock__center simple-analog-clock__center--top" />
</template>
</div>
</template>
<style scoped>
.simple-analog-clock {
position: relative;
flex: 0 0 auto;
}
.simple-analog-clock__face-shadow,
.simple-analog-clock__face {
position: absolute;
border-radius: 50%;
background: #ffffff;
}
.simple-analog-clock__face-shadow {
inset: -2.4px;
box-shadow: 0 1.8px 5.4px rgba(0, 0, 0, 0.3);
}
.simple-analog-clock__face {
inset: 0;
border: 1.6px solid #1e1e1e;
}
.simple-analog-clock__tick {
position: absolute;
top: 50%;
left: 50%;
background: #1e1e1e;
}
.simple-analog-clock__hand {
position: absolute;
top: 50%;
left: 50%;
background: #000000;
}
.simple-analog-clock__hand--minute {
box-shadow: 0 3.6px 10.8px rgba(0, 0, 0, 0.4);
}
.simple-analog-clock__hand--hour {
box-shadow: 2.4px 2.4px 10.8px rgba(0, 0, 0, 0.4);
}
.simple-analog-clock__center {
position: absolute;
top: 50%;
left: 50%;
border-radius: 50%;
transform: translate(-50%, -50%);
background: #000000;
}
.simple-analog-clock__center--bottom {
width: 4.8px;
height: 4.8px;
}
.simple-analog-clock__center--top {
width: 9.6px;
height: 9.6px;
}
</style>