写在前面
上一篇介绍使用CSS+JS方式实现,但元素太过单一。此篇将以HTML5的canvas标签结合JS来实现。
HTML代码
<canvas id="clock" width="300" height="300"></canvas>
JS代码
var canvas = null;
var ctx = null;
var interval = null; //计时器
var clockRadius = null; //钟表半径
var hoursCalibrationWidth = 4; //时针刻度宽度
var minutesCalibrationWidth = 1; //分针刻度宽度
var hoursLineWidth = 6; //时针宽度
var minutesLineWidth = 4; //分针宽度
var secondsLineWidth = 2; //秒针宽度window.addEventListener("visibilitychange", function (event) {if (document.hidden) {window.clearInterval(interval);interval = null;} else {startDraw();}
})window.addEventListener("load", function () {canvas = document.getElementById('clock');if (!canvas.getContext) {return;}//去除透明度ctx = canvas.getContext('2d', { alpha: true });//移动中心ctx.translate(canvas.width / 2, canvas.height / 2);//设置钟表半径clockRadius = Math.min(canvas.width, canvas.height) / 2 * 0.95;//调整路径结束点样式ctx.lineCap = "round";//开始绘制startDraw();
});function startDraw() {drawClock();interval = setInterval(drawClock, 1000);
}function drawClock() {// 清除上一次样式ctx.clearRect(-clockRadius, -clockRadius, 2 * clockRadius, 2 * clockRadius);ctx.fillStyle = "rgba(0,0,0,0.1)";ctx.arc(0, 0, clockRadius, 0, 2 * Math.PI);ctx.fill();drawCalibration();drawHourText();drawDateTime();drawEdge(clockRadius);drawCenterCicle();
}function drawEdge(radius) {ctx.beginPath();ctx.arc(0, 0, radius, 0, 2 * Math.PI);const grad = ctx.createRadialGradient(0, 0, radius * 0.95, 0, 0, radius * 1.05);grad.addColorStop(0, '#eee');grad.addColorStop(0.1, '#fff');grad.addColorStop(0.3, '#eee');grad.addColorStop(0.5, '#666');grad.addColorStop(0.7, '#eee');grad.addColorStop(0.9, '#fff');grad.addColorStop(1, '#fff');ctx.strokeStyle = grad;ctx.lineWidth = radius * 0.1;ctx.stroke();
}//画时钟圆心
function drawCenterCicle() {ctx.beginPath();ctx.arc(0, 0, clockRadius * 0.03, 0, 2 * Math.PI);ctx.lineWidth = 1;ctx.strokeStyle = "gray";ctx.stroke();ctx.beginPath();ctx.arc(0, 0, clockRadius * 0.03, 0, 2 * Math.PI);ctx.fillStyle = "#eee";ctx.fill();ctx.beginPath();ctx.fillStyle = "red";ctx.arc(0, 0, clockRadius * 0.02, 0, 2 * Math.PI);ctx.fill();
}//画时间
function drawDateTime() {ctx.strokeStyle = "black";const date = new Date();drawDateAndLunar(date);drawTime(ctx, clockRadius, date);
}// 画时间时分秒表针
function drawTime(ctx, radius, now) {let hour = now.getHours();let minute = now.getMinutes();let second = now.getSeconds();// 时hour = hour % 12;hour = (hour * Math.PI / 6) + (minute * Math.PI / (6 * 60)) + (second * Math.PI / (360 * 60));drawHand(ctx, hour, radius * 0.5, radius * 0.05);// 分minute = (minute * Math.PI / 30) + (second * Math.PI / (30 * 60));drawHand(ctx, minute, radius * 0.65, radius * 0.02);// 秒second = (second * Math.PI / 30);drawHand(ctx, second, radius * 0.88, radius * 0.005, 'red');
}// 画表针
function drawHand(ctx, pos, length, width, color = 'black') {ctx.beginPath();ctx.lineWidth = width;ctx.lineCap = "round";ctx.moveTo(0, 0);ctx.rotate(pos);ctx.lineTo(0, -length);ctx.strokeStyle = color;ctx.stroke();ctx.rotate(-pos);
}//画时间
function drawHourText() {ctx.font = "20px Arial";ctx.textAlign = "center";ctx.textBaseline = "middle";for (var i = 1; i <= 12; i++) {ctx.fillStyle = i % 6 == 0 ? "blue" : "#eee";var theta = (i - 3) * (Math.PI * 2) / 12;var x = clockRadius * 0.75 * Math.cos(theta);var y = clockRadius * 0.75 * Math.sin(theta);ctx.fillText(i, x, y);}
}//画刻度线
function drawCalibration() {for (var i = 0; i < 60; i++) {ctx.beginPath();var theta = i * (Math.PI * 2) / 60;var x = clockRadius * Math.cos(theta);var y = clockRadius * Math.sin(theta);ctx.moveTo(x, y);if (i % 5 == 0) {ctx.strokeStyle = i % (12 + 3) == 0 ? "red" : "black";ctx.lineWidth = hoursCalibrationWidth;ctx.lineTo(0.91 * x, 0.91 * y);} else {ctx.strokeStyle = "#eee";ctx.lineWidth = minutesCalibrationWidth;ctx.lineTo(0.92 * x, 0.92 * y);}ctx.stroke();}
}// 画日期及农历
function drawDateAndLunar(date) {// 绘制周ctx.beginPath();ctx.rect(clockRadius * 0.4, -clockRadius * 0.05, clockRadius * 0.1, clockRadius * 0.1)ctx.lineWidth = 0.2;ctx.strokeStyle = '#fff';ctx.stroke();ctx.beginPath();ctx.font = clockRadius * 0.08 + 'px ms';ctx.fillStyle = "#fff";ctx.textAlign = "center";ctx.fillText(weekZh(date.getDay()), clockRadius * 0.45, -clockRadius * 0.0001);// 绘制公历日期const datestr = date.getFullYear()+ '-'+ (date.getMonth() + 1).toString().padStart(2, '0')+ '-'+ date.getDate().toString().padStart(2, '0');ctx.beginPath();ctx.font = clockRadius * 0.08 + 'px ms';ctx.fillStyle = "#ffe";ctx.textAlign = "center";ctx.fillText(datestr, 0, clockRadius * 0.4);// 绘制农历日期const dateLunarstr = transDayToLunarZh(date);ctx.beginPath();ctx.font = clockRadius * 0.08 + 'px ms';ctx.fillStyle = "#eee";ctx.textAlign = "center";ctx.fillText(dateLunarstr, 0, clockRadius * 0.5);
}// 周转中文
function weekZh(weeknum) {const weeks = ['一', '二', '三', '四', '五', '六', '日'];return weeks[weeknum - 1];
}// 农历日期处理
function transDayToLunarZh(date) {let map = new Map([['1', '初一'],['2', '初二'],['3', '初三'],['4', '初四'],['5', '初五'],['6', '初六'],['7', '初七'],['8', '初八'],['9', '初九'],['10', '初十'],['11', '十一'],['12', '十二'],['13', '十三'],['14', '十四'],['15', '十五'],['16', '十六'],['17', '十七'],['18', '十八'],['19', '十九'],['20', '廿'],['21', '廿一'],['22', '廿二'],['23', '廿三'],['24', '廿四'],['25', '廿五'],['26', '廿六'],['27', '廿七'],['28', '廿八'],['29', '廿九'],['30', '卅'],]);const dateLunarstr = date.toLocaleString('zh-Hans-u-ca-chinese')// 转成农历.match(/(.*?)\s/)[1]// 提取日期部分.match(/年(\S*)/)[1];// 提取年之后的日期// 提取月份 及 日转换为中文let lunarMonth = dateLunarstr.match(/(.*?)月/)[0];let lunarDay = dateLunarstr.match(/月(\d+)/)[1];return lunarMonth + map.get(lunarDay.toString());
}