// 定义二进制数字类型 type BinaryDigits = Record; type CanvasRef = HTMLCanvasElement | null; /** * 数字电子表Hook * @returns {Object} - 包含canvas引用和初始化方法 */ export const useDigitalWatch = () => { const canvasRef = ref(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 } } }