payment-statistics/src/composables/useDigitalWatch.ts

214 lines
6.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// 定义二进制数字类型
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
}
}
}