165 lines
4.7 KiB
Vue
165 lines
4.7 KiB
Vue
<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>
|