214 lines
6.5 KiB
TypeScript
214 lines
6.5 KiB
TypeScript
|
||
// 定义二进制数字类型
|
||
type BinaryDigits = Record<string, boolean[]>;
|
||
type CanvasRef = HTMLCanvasElement | null;
|
||
|
||
/**
|
||
* 数字电子表Hook
|
||
* @returns {Object} - 包含canvas引用和初始化方法
|
||
*/
|
||
export const useDigitalWatch = () => {
|
||
const canvasRef = ref<CanvasRef>(null)
|
||
let animationTimer: number | null = null
|
||
|
||
// 二进制表示数字的配置
|
||
const binary: BinaryDigits = {
|
||
"0": [true, true, true, false, true, true, true],
|
||
"1": [false, false, true, false, false, true, false],
|
||
"2": [true, false, true, true, true, false, true],
|
||
"3": [true, false, true, true, false, true, true],
|
||
"4": [false, true, true, true, false, true, false],
|
||
"5": [true, true, false, true, false, true, true],
|
||
"6": [true, true, false, true, true, true, true],
|
||
"7": [true, true, true, false, false, true, false],
|
||
"8": [true, true, true, true, true, true, true],
|
||
"9": [true, true, true, true, false, true, false]
|
||
}
|
||
|
||
// 样式配置
|
||
let on = "#44F1FF" // 激活颜色
|
||
let off = "#031743" // 未激活颜色
|
||
|
||
// 缩放比例
|
||
let scale = 1
|
||
|
||
// 工具函数 - 格式化数字为两位(补零)
|
||
const formatZero = (n: number): string => {
|
||
return n >= 10 ? n.toString() : `0${n}`
|
||
}
|
||
|
||
// 渲染数字的一个区段
|
||
const renderDigit = (ctx: CanvasRenderingContext2D, x = 0, y = 0, binaryPattern: boolean[]) => {
|
||
const s = scale
|
||
// top
|
||
ctx.beginPath()
|
||
ctx.moveTo(x + 20 * s, y + 5 * s)
|
||
ctx.lineTo(x + 35 * s, y)
|
||
ctx.lineTo(x + 105 * s, y)
|
||
ctx.lineTo(x + 120 * s, y + 5 * s)
|
||
ctx.lineTo(x + 105 * s, y + 20 * s)
|
||
ctx.lineTo(x + 35 * s, y + 20 * s)
|
||
ctx.lineTo(x + 20 * s, y + 5 * s)
|
||
ctx.fillStyle = binaryPattern[0] ? on : off
|
||
ctx.fill()
|
||
|
||
// top-left
|
||
ctx.beginPath()
|
||
ctx.moveTo(x + 15 * s, y + 10 * s)
|
||
ctx.lineTo(x + 10 * s, y + 25 * s)
|
||
ctx.lineTo(x + 10 * s, y + 95 * s)
|
||
ctx.lineTo(x + 15 * s, y + 110 * s)
|
||
ctx.lineTo(x + 30 * s, y + 95 * s)
|
||
ctx.lineTo(x + 30 * s, y + 25 * s)
|
||
ctx.fillStyle = binaryPattern[1] ? on : off
|
||
ctx.fill()
|
||
|
||
// top-right
|
||
ctx.beginPath()
|
||
ctx.moveTo(x + 125 * s, y + 10 * s)
|
||
ctx.lineTo(x + 130 * s, y + 25 * s)
|
||
ctx.lineTo(x + 130 * s, y + 95 * s)
|
||
ctx.lineTo(x + 125 * s, y + 110 * s)
|
||
ctx.lineTo(x + 110 * s, y + 95 * s)
|
||
ctx.lineTo(x + 110 * s, y + 25 * s)
|
||
ctx.fillStyle = binaryPattern[2] ? on : off
|
||
ctx.fill()
|
||
|
||
// middle
|
||
ctx.beginPath()
|
||
ctx.moveTo(x + 20 * s, y + 115 * s)
|
||
ctx.lineTo(x + 35 * s, y + 100 * s)
|
||
ctx.lineTo(x + 105 * s, y + 100 * s)
|
||
ctx.lineTo(x + 120 * s, y + 115 * s)
|
||
ctx.lineTo(x + 105 * s, y + 130 * s)
|
||
ctx.lineTo(x + 35 * s, y + 130 * s)
|
||
ctx.fillStyle = binaryPattern[3] ? on : off
|
||
ctx.fill()
|
||
|
||
// bottom-left
|
||
ctx.beginPath()
|
||
ctx.moveTo(x + 15 * s, y + 120 * s)
|
||
ctx.lineTo(x + 10 * s, y + 135 * s)
|
||
ctx.lineTo(x + 10 * s, y + 205 * s)
|
||
ctx.lineTo(x + 15 * s, y + 220 * s)
|
||
ctx.lineTo(x + 30 * s, y + 205 * s)
|
||
ctx.lineTo(x + 30 * s, y + 135 * s)
|
||
ctx.fillStyle = binaryPattern[4] ? on : off
|
||
ctx.fill()
|
||
|
||
// bottom-right
|
||
ctx.beginPath()
|
||
ctx.moveTo(x + 125 * s, y + 120 * s)
|
||
ctx.lineTo(x + 130 * s, y + 135 * s)
|
||
ctx.lineTo(x + 130 * s, y + 205 * s)
|
||
ctx.lineTo(x + 125 * s, y + 220 * s)
|
||
ctx.lineTo(x + 110 * s, y + 205 * s)
|
||
ctx.lineTo(x + 110 * s, y + 135 * s)
|
||
ctx.fillStyle = binaryPattern[5] ? on : off
|
||
ctx.fill()
|
||
|
||
// bottom
|
||
ctx.beginPath()
|
||
ctx.moveTo(x + 20 * s, y + 225 * s)
|
||
ctx.lineTo(x + 35 * s, y + 210 * s)
|
||
ctx.lineTo(x + 105 * s, y + 210 * s)
|
||
ctx.lineTo(x + 120 * s, y + 225 * s)
|
||
ctx.lineTo(x + 105 * s, y + 235 * s)
|
||
ctx.lineTo(x + 35 * s, y + 235 * s)
|
||
ctx.lineTo(x + 20 * s, y + 225 * s)
|
||
ctx.fillStyle = binaryPattern[6] ? on : off
|
||
ctx.fill()
|
||
}
|
||
|
||
// 计算合适的缩放比例
|
||
const calculateScale = (width: number, height: number): number => {
|
||
// 原始设计下,整个表宽约为940,高约为240
|
||
const originalWidth = 940
|
||
const originalHeight = 240
|
||
|
||
const scaleX = width / originalWidth
|
||
const scaleY = height / originalHeight
|
||
|
||
// 取较小的缩放比例,确保完全显示
|
||
return Math.min(scaleX, scaleY) * 0.9 // 稍微缩小一点,留出边距
|
||
}
|
||
|
||
// 绘制时间
|
||
const drawTime = () => {
|
||
if (!canvasRef.value) return
|
||
|
||
const canvas = canvasRef.value
|
||
const ctx = canvas.getContext('2d')
|
||
if (!ctx) return
|
||
|
||
// 清除画布
|
||
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
||
|
||
// 获取当前时间
|
||
const date = new Date()
|
||
|
||
// 格式化时间数字
|
||
const [hour_ten_digit, hour_one_digit] = formatZero(date.getHours()).split("")
|
||
const [minute_ten_digit, minute_one_digit] = formatZero(date.getMinutes()).split("")
|
||
const [second_ten_digit, second_one_digit] = formatZero(date.getSeconds()).split("")
|
||
|
||
// 计算中心位置,使表居中
|
||
const centerY = (canvas.height - 240 * scale) / 2 + 20 * scale
|
||
const startX = (canvas.width - 940 * scale) / 2 + 20 * scale
|
||
|
||
// 使用指定的间距渲染每个数字
|
||
const spacing = 140 * scale
|
||
|
||
// 渲染每个数字
|
||
renderDigit(ctx, startX, centerY, binary[hour_ten_digit])
|
||
renderDigit(ctx, startX + spacing, centerY, binary[hour_one_digit])
|
||
renderDigit(ctx, startX + spacing * 2.5, centerY, binary[minute_ten_digit])
|
||
renderDigit(ctx, startX + spacing * 3.5, centerY, binary[minute_one_digit])
|
||
renderDigit(ctx, startX + spacing * 5, centerY, binary[second_ten_digit])
|
||
renderDigit(ctx, startX + spacing * 6, centerY, binary[second_one_digit])
|
||
}
|
||
|
||
// 初始化函数
|
||
const initWatch = (width = 1000, height = 300, interval = 500) => {
|
||
if (!canvasRef.value) return
|
||
|
||
const canvas = canvasRef.value
|
||
canvas.width = width
|
||
canvas.height = height
|
||
|
||
// 计算适合的缩放比例
|
||
scale = calculateScale(width, height)
|
||
|
||
// 启动定时器更新时间显示
|
||
drawTime() // 立即首次绘制
|
||
animationTimer = window.setInterval(() => {
|
||
drawTime()
|
||
}, interval)
|
||
}
|
||
|
||
// 停止计时器
|
||
const stopWatch = () => {
|
||
if (animationTimer) {
|
||
clearInterval(animationTimer)
|
||
animationTimer = null
|
||
}
|
||
}
|
||
|
||
// 自动清理
|
||
onBeforeUnmount(() => {
|
||
stopWatch()
|
||
})
|
||
|
||
// 返回需要暴露的内容
|
||
return {
|
||
canvasRef,
|
||
initWatch,
|
||
stopWatch,
|
||
drawTime,
|
||
// 自定义选项
|
||
setColors: (onColor: string, offColor: string) => {
|
||
on = onColor
|
||
off = offColor
|
||
}
|
||
}
|
||
}
|