使用canvas绘制一个动态的表盘

使用canvas绘制一个动态的表盘

技术要求

需要一点点数学基础
需要对 canvas 的常见的方法熟悉

一点点数学基础

角度转弧度的计算公式

canvas 常见的方法

菜鸟教程

扬帆起航

  1. 首先创建一个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;
  1. 绘制表盘

方法名称: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();
  1. 绘制表盘上的日期

方法名称: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();
  1. 绘制表盘中心的小圆点

方法名称: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();
  1. 绘制时分秒针

方法名称: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); // 绘制小圆点
  1. 整合并调用方法,渲染表盘
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>

最终成品

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值