使用canvas绘制一个动态的表盘
技术要求
需要一点点数学基础
需要对canvas
的常见的方法熟悉
一点点数学基础
canvas 常见的方法
扬帆起航
- 首先创建一个
canvas
,并将这个dom添加在html中
方法名称:
createCanvas
// 创建一个 canvas circle
const canvas = document.createElement('canvas');
// 防止一些浏览器不支持,给与提示
canvas.innerText = '您的浏览器版本过低,请升级你的浏览器!';
// 使用原生的方式设置 canvas 的 高度和宽度,css设置canvas并不是很可靠
canvas.width = 800;
canvas.height = 800;
canvas.style.border = '1px #ccc solid';
return canvas;
- 绘制表盘
方法名称:
drawDial
const { width, height } = canvas;
// 获得 2d 上下文对象
const ctx = canvas.getContext('2d');
const { PI } = Math;
const r = (width / 2) - 30;
// 还原设置
ctx.restore();
// 清空画布
ctx.clearRect(0, 0, width, height);
ctx.save();
// 定位到中心点
ctx.translate(width / 2, height / 2);
ctx.beginPath();
ctx.lineWidth = 4;
// 以0,0为原点,r为半径,0为起始角,2*Math.PI为结束角,顺时针画圆
ctx.arc(0, 0, r, 0, 2 * PI, false);
ctx.stroke();
// 绘制刻度
ctx.beginPath();
ctx.lineWidth = 2;
for (let i = 0; i < 60; i += 1) {
ctx.moveTo(0, 0);
ctx.arc(0, 0, r, (6 * i * Math.PI) / 180, 6 * (i + 1) * (Math.PI / 180), false);
}
ctx.closePath();
ctx.stroke();
// 画实心圆
ctx.beginPath();
ctx.fillStyle = '#fff';
ctx.arc(0, 0, r - 30, 0, (360 * Math.PI) / 180, false);
ctx.fill();
// 画时针刻度线
ctx.beginPath();
ctx.lineWidth = 10;
for (let i = 0; i < 12; i += 1) {
ctx.moveTo(0, 0);
ctx.arc(0, 0, r, (30 * i * Math.PI) / 180, 30 * (i + 1) * (Math.PI / 180), false);
}
ctx.closePath();
ctx.stroke();
// 画实心圆
ctx.beginPath();
ctx.fillStyle = '#fff';
ctx.arc(0, 0, r - 60 / 2, 0, (360 * Math.PI) / 180, false);
ctx.fill();
// 绘制小时刻盘
// 弧度=角度*Math.PI/180;
const deg = (2 * Math.PI) / 12;
ctx.beginPath();
ctx.fillStyle = 'black';// 字体颜色
ctx.font = '60px noraml Calibri';// 字体
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
for (let i = 1; i < 13; i += 1) {
const x1 = Math.sin(i * deg);// 正弦
const y1 = -Math.cos(i * deg);// 余弦
ctx.fillText(i, x1 * (r - 80), y1 * (r - 80));// 填充 80这个值越大 越显示在圆外面
}
ctx.closePath();
ctx.restore();
- 绘制表盘上的日期
方法名称:
drawDate
const { width, height } = canvas;
const ctx = canvas.getContext('2d');
const year = date.getFullYear();
const month = date.getMonth() < 9 ? `0${date.getMonth()}` : date.getMonth() + 1;
const day = date.getDate();
const hours = date.getHours() < 10 ? `0${date.getHours()}` : date.getHours();
const minutes = date.getMinutes() < 10 ? `0${date.getMinutes()}` : date.getMinutes();
const seconds = date.getSeconds() < 10 ? `0${date.getSeconds()}` : date.getSeconds();
const dateStr = `${year}/${month}/${day}`;
const time = `${hours}:${minutes}:${seconds}`;
ctx.save();
ctx.font = '40px LED';
ctx.textAlign = 'center';
ctx.fillStyle = '#ccc';
// ctx.strokeStyle = '#ccc';
// ctx.strokeText(time, width / 2, (height / 2) + 200, width);
ctx.fillText(dateStr, width / 2, (height / 2) + 170, width);
ctx.fillText(time, width / 2, (height / 2) + 225, width);
ctx.restore();
- 绘制表盘中心的小圆点
方法名称:
drawDot
const { width, height } = canvas;
const ctx = canvas.getContext('2d');
ctx.save();
ctx.beginPath();
ctx.fillStyle = '#F3A829';
ctx.arc(width / 2, height / 2, 10, 0, 360, false);
ctx.fill();
ctx.closePath();
ctx.restore();
- 绘制时分秒针
方法名称:
drawAllHands
// 渲染当前的时间
const date = new Date();
//弧度=角度*Math.PI/180;
// 因为现在的0度=3点,第一步需要重置这个0度到12点
// 每小时的弧度是30度
const hoursRadian = ((date.getHours() * 30 - 90) * Math.PI) / 180;
// 每分钟的弧度是6度
const minutesRadian = ((date.getMinutes() * 6 - 90) * Math.PI) / 180;
// 每秒钟的弧度是6度
const secondsRadian = ((date.getSeconds() * 6 - 90) * Math.PI) / 180;
drawDate(date, canvas); // 绘制日期时间
drawHand(hoursRadian, 150, 10, 'black', canvas); // 绘制时针
drawHand(minutesRadian, 230, 6, 'black', canvas); // 绘制分针
drawHand(secondsRadian, 280, 3, '#F3A829', canvas); // 绘制秒针
drawDot(canvas); // 绘制小圆点
- 整合并调用方法,渲染表盘
const app = document.getElementById('app');
const canvas = createCanvas();
// 将 canvas appendChild 到 #app
app.appendChild(canvas);
requestAnimationFrame(function step() {
drawDial(canvas); // 绘制表盘
drawAllHands(canvas); // 绘制时分秒针
requestAnimationFrame(step);
});
完整js代码
/* 绘制时针、或分针、或秒针
* 参数1:要绘制的针的角度
* 参数2:要绘制的针的长度
* 参数3:要绘制的针的宽度
* 参数4:要绘制的针的颜色
* 参数4:ctx
*/
function drawHand(angle, len, lineWidth, color, canvas) {
const { width, height } = canvas;
// 获得 2d 上下文对象
const ctx = canvas.getContext('2d');
ctx.save();
// 定位到中心点
ctx.translate(width / 2, height / 2);
ctx.shadowColor = '#000';
ctx.shadowOffsetY = 30;
ctx.shadowOffsetX = 30;
ctx.shadowBlur = 30;
ctx.beginPath();
ctx.lineWidth = lineWidth;
ctx.strokeStyle = color;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
// ctx.moveTo(0, 0);
// ctx.arc(0, 0, len, angle, angle, false);
ctx.rotate(angle);
ctx.moveTo(0, 0);
ctx.lineTo(-20, 0);
ctx.lineTo(len + 20, 0);
ctx.fill();
ctx.stroke();
ctx.closePath();
ctx.restore();
}
/* 绘制表盘 */
function drawDial(canvas) {
const { width, height } = canvas;
// 获得 2d 上下文对象
const ctx = canvas.getContext('2d');
const { PI } = Math;
const r = (width / 2) - 30;
// 还原设置
ctx.restore();
// 清空画布
ctx.clearRect(0, 0, width, height);
ctx.save();
// 定位到中心点
ctx.translate(width / 2, height / 2);
ctx.beginPath();
ctx.lineWidth = 4;
// 以0,0为原点,r为半径,0为起始角,2*Math.PI为结束角,顺时针画圆
ctx.arc(0, 0, r, 0, 2 * PI, false);
ctx.stroke();
// 绘制刻度
ctx.beginPath();
ctx.lineWidth = 2;
for (let i = 0; i < 60; i += 1) {
ctx.moveTo(0, 0);
ctx.arc(0, 0, r, (6 * i * Math.PI) / 180, 6 * (i + 1) * (Math.PI / 180), false);
}
ctx.closePath();
ctx.stroke();
// 画实心圆
ctx.beginPath();
ctx.fillStyle = '#fff';
ctx.arc(0, 0, r - 30, 0, (360 * Math.PI) / 180, false);
ctx.fill();
// 画时针刻度线
ctx.beginPath();
ctx.lineWidth = 10;
for (let i = 0; i < 12; i += 1) {
ctx.moveTo(0, 0);
ctx.arc(0, 0, r, (30 * i * Math.PI) / 180, 30 * (i + 1) * (Math.PI / 180), false);
}
ctx.closePath();
ctx.stroke();
// 画实心圆
ctx.beginPath();
ctx.fillStyle = '#fff';
ctx.arc(0, 0, r - 60 / 2, 0, (360 * Math.PI) / 180, false);
ctx.fill();
// 绘制小时刻盘
// 弧度=角度*Math.PI/180;
const deg = (2 * Math.PI) / 12;
ctx.beginPath();
ctx.fillStyle = 'black';// 字体颜色
ctx.font = '60px noraml Calibri';// 字体
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
for (let i = 1; i < 13; i += 1) {
const x1 = Math.sin(i * deg);// 正弦
const y1 = -Math.cos(i * deg);// 余弦
ctx.fillText(i, x1 * (r - 80), y1 * (r - 80));// 填充 80这个值越大 越显示在圆外面
}
ctx.closePath();
ctx.restore();
}
/**
* 表盘上的日期
*/
function drawDate(date, canvas) {
const { width, height } = canvas;
const ctx = canvas.getContext('2d');
const year = date.getFullYear();
const month = date.getMonth() < 9 ? `0${date.getMonth()}` : date.getMonth() + 1;
const day = date.getDate();
const hours = date.getHours() < 10 ? `0${date.getHours()}` : date.getHours();
const minutes = date.getMinutes() < 10 ? `0${date.getMinutes()}` : date.getMinutes();
const seconds = date.getSeconds() < 10 ? `0${date.getSeconds()}` : date.getSeconds();
const dateStr = `${year}/${month}/${day}`;
const time = `${hours}:${minutes}:${seconds}`;
ctx.save();
ctx.font = '40px LED';
ctx.textAlign = 'center';
ctx.fillStyle = '#ccc';
// ctx.strokeStyle = '#ccc';
// ctx.strokeText(time, width / 2, (height / 2) + 200, width);
ctx.fillText(dateStr, width / 2, (height / 2) + 170, width);
ctx.fillText(time, width / 2, (height / 2) + 225, width);
ctx.restore();
}
/** 绘制小圆点 */
function drawDot(canvas) {
const { width, height } = canvas;
const ctx = canvas.getContext('2d');
ctx.save();
ctx.beginPath();
ctx.fillStyle = '#F3A829';
ctx.arc(width / 2, height / 2, 10, 0, 360, false);
ctx.fill();
ctx.closePath();
ctx.restore();
}
/* 绘制时分秒针 */
function drawAllHands(canvas) {
// 渲染当前的时间
const date = new Date();
// 弧度=角度*Math.PI/180;
// 因为现在的0度=3点,第一步需要重置这个0度到12点
// 每小时的弧度是30度
const hoursRadian = ((date.getHours() * 30 - 90) * Math.PI) / 180;
// 每分钟的弧度是6度
const minutesRadian = ((date.getMinutes() * 6 - 90) * Math.PI) / 180;
// 每秒钟的弧度是6度
const secondsRadian = ((date.getSeconds() * 6 - 90) * Math.PI) / 180;
drawDate(date, canvas); // 绘制日期时间
drawHand(hoursRadian, 150, 10, 'black', canvas); // 绘制时针
drawHand(minutesRadian, 230, 6, 'black', canvas); // 绘制分针
drawHand(secondsRadian, 280, 3, '#F3A829', canvas); // 绘制秒针
drawDot(canvas); // 绘制小圆点
}
/**
* 创建canvas
*/
function createCanvas() {
// 创建一个 canvas circle
const canvas = document.createElement('canvas');
// 防止一些浏览器不支持,给与提示
canvas.innerText = '您的浏览器版本过低,请升级你的浏览器!';
// 使用原生的方式设置 canvas 的 高度和宽度,css设置canvas并不是很可靠
canvas.width = 800;
canvas.height = 800;
canvas.style.border = '1px #ccc solid';
return canvas;
}
function draw(canvas) {
requestAnimationFrame(function step() {
drawDial(canvas); // 绘制表盘
drawAllHands(canvas); // 绘制时分秒针
requestAnimationFrame(step);
});
}
const app = document.getElementById('app');
const canvas = createCanvas();
// 将 canvas appendChild 到 #app
app.appendChild(canvas);
draw(canvas);
备注
这里引用了特殊字体,
canvas
引用特殊字体很简单,本案例中使用的字体下载地址
<style>
@font-face {
font-family: 'LED';
/* 这里是字体文件的路径 */
src: url('./font//bb3812/bb3812.TTF');
}
</style>