<style>
.clock_wrapper {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
min-height: 640px;
background: #f0f0f0;
margin: 0 auto;
}
canvas {
box-shadow: 0 0 20px rgba(0,0,0,0.1);
border-radius: 50%;
background: white;
}
</style>
<div class="clock_wrapper">
<canvas id="clock" width="400" height="400"></canvas>
</div>
<script>
const canvas = document.getElementById('clock');
const ctx = canvas.getContext('2d');
// 高清屏适配
const scale = window.devicePixelRatio || 1;
canvas.width = 400 * scale;
canvas.height = 400 * scale;
ctx.scale(scale, scale);
function drawClock() {
// 清除画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 获取当前时间
const now = new Date();
const hours = now.getHours() % 12;
const minutes = now.getMinutes();
const seconds = now.getSeconds();
const milliseconds = now.getMilliseconds();
// 表盘样式
const centerX = canvas.width / 2 / scale;
const centerY = canvas.height / 2 / scale;
const radius = Math.min(centerX, centerY) - 10;
// 绘制表盘背景
const gradient = ctx.createRadialGradient(
centerX, centerY, 0, centerX, centerY, radius
);
gradient.addColorStop(0, '#f8f8f8');
gradient.addColorStop(1, '#e0e0e0');
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
ctx.fill();
// 绘制边框
ctx.strokeStyle = 'linear-gradient(45deg, #666, #999)';
ctx.lineWidth = 8;
ctx.beginPath();
ctx.arc(centerX, centerY, radius - 4, 0, Math.PI * 2);
ctx.stroke();
// 绘制小时刻度
ctx.lineWidth = 3;
ctx.strokeStyle = '#333';
for (let i = 0; i < 12; i++) {
ctx.save();
ctx.translate(centerX, centerY);
ctx.rotate(i * 30 * Math.PI / 180);
ctx.beginPath();
ctx.moveTo(0, -radius + 20);
ctx.lineTo(0, -radius + 40);
ctx.stroke();
ctx.restore();
}
// 绘制分钟刻度
ctx.lineWidth = 1;
ctx.strokeStyle = '#666';
for (let i = 0; i < 60; i++) {
if (i % 5 === 0) continue; // 跳过小时刻度
ctx.save();
ctx.translate(centerX, centerY);
ctx.rotate(i * 6 * Math.PI / 180);
ctx.beginPath();
ctx.moveTo(0, -radius + 25);
ctx.lineTo(0, -radius + 35);
ctx.stroke();
ctx.restore();
}
// 绘制数字
ctx.font = '16px Arial';
ctx.fillStyle = '#333';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
for (let i = 1; i <= 12; i++) {
const angle = i * 30 * Math.PI / 180;
const x = centerX + Math.sin(angle) * (radius - 60);
const y = centerY - Math.cos(angle) * (radius - 60);
ctx.fillText(i.toString(), x, y);
}
// 计算指针角度
const secondAngle = (seconds + milliseconds / 1000) * 6; // 每秒6度
const minuteAngle = (minutes + seconds / 60) * 6;
const hourAngle = (hours + minutes / 60) * 30;
// 绘制时针
ctx.save();
ctx.translate(centerX, centerY);
ctx.rotate(hourAngle * Math.PI / 180);
ctx.fillStyle = '#333';
ctx.shadowColor = 'rgba(0,0,0,0.2)';
ctx.shadowBlur = 8;
ctx.shadowOffsetY = 3;
ctx.beginPath();
ctx.moveTo(-4, 0);
ctx.lineTo(0, -radius * 0.5);
ctx.lineTo(4, 0);
ctx.arc(0, 0, 4, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
// 绘制分针
ctx.save();
ctx.translate(centerX, centerY);
ctx.rotate(minuteAngle * Math.PI / 180);
ctx.fillStyle = '#666';
ctx.beginPath();
ctx.moveTo(-3, 0);
ctx.lineTo(0, -radius * 0.7);
ctx.lineTo(3, 0);
ctx.arc(0, 0, 3, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
// 绘制秒针
ctx.save();
ctx.translate(centerX, centerY);
ctx.rotate(secondAngle * Math.PI / 180);
ctx.strokeStyle = '#e74c3c';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(0, radius * 0.1);
ctx.lineTo(0, -radius * 0.8);
ctx.stroke();
ctx.restore();
// 绘制中心圆点
ctx.beginPath();
ctx.fillStyle = '#e74c3c';
ctx.arc(centerX, centerY, 4, 0, Math.PI * 2);
ctx.fill();
requestAnimationFrame(drawClock);
}
drawClock();
</script>