使用 Canvas 制作时钟
2019-07-21 本文已影响5人
CondorHero
如果你还不会 canvas 请参我的文章: Canvas 使用指南和几个套路
其实已经使用 canvas 写了好几个游戏了,虽然写好了,但是不太想整理出来,我对自己也没办法了,太懒了自己写完的代码,总感觉自己会了就不去看了,不过自制力还不错,慢慢的整理出来,分享一下。
这次做的是 canvas 时钟,并不是太难。主要涉及两个知识点:
- JS 时间对象的操作
- 圆周的坐标
先来解决圆周的坐标,看图很明显,圆周内的六十个小圆圈和十二个数字均是通过确定圆周上的坐标来确定的。关键就是怎么确定每个坐标的(x,y)。用到的方法就是初中的数学知识「直角三角形的正弦函数和余弦函数」,所以这题就完全可以改成这样一道数学题:
已知直角三角形的斜边(圆的半径)和其中一个锐角,求三角形另外两个边?
这题简单吧,我们求出来另外两个边就是我们需要的(x,y)。
如果你还不明白请参考我的另一篇文章:
简单实现小球的圆周运动:https://www.jianshu.com/p/43dd74ebb681
注意本例使用的语法全部为 ES6 的语法,圆周坐标的核心语法:
for(let i = 1;i < 13;i++){
// 每个数字对应的角度
let delta = (i - 3) * (2 * Math.PI) / 12;
//我们需要的指针长度比圆的半径小myCanvas.width / 2 * 0.85
let x = myCanvas.width / 2 * 0.85 * Math.cos(delta);
let y = myCanvas.height / 2 * 0.85 * Math.sin(delta);
ctx.font = "bold 20px Arail";
// 文字水平居中
ctx.textAlign = "center";
// 文字垂直居中
ctx.textBaseline = 'middle';
ctx.fillText(i,x,y);
}
下面来看第二个难点时间:
创建时间和得到时间不难,难就难在秒针动的时候,怎么让分针和时针也跟着动,秒针是一直在前进的,在没超过一分钟内取到的分和小时都是不变的,既然取到的数字是不变的,根据数字计算的旋转角度也是不变的,现在要做的就是秒针动一下,分别给时针和分针加上一点旋转角度。核心的就是这两句。不必担心秒针走完六十下,从零开始,数值没有加上,当超过六十的时候这时分针已经更新了时间。
// 分钟改变对应的小时改变量
this.hour = this.hours + this.minutes / 60;
// 秒钟改变对应的分钟改变量
this.minute = this.minutes + this.seconds / 60;
// 得到系统的时间
createDate(){
// 得到现在的时间对象
this.date = new Date();
// 分别得到时,分,秒
this.hours = this.date.getHours();
this.minutes = this.date.getMinutes();
this.seconds = this.date.getSeconds();
ctx.font="bold 18px 宋体";
ctx.fillStyle=`rgb(${~~(Math.random()*255)},${~~(Math.random()*255)},${~~(Math.random()*255)})`;
ctx.fillText(`${this.hours}:${this.minutes}:${this.seconds}`,myCanvas.width / 6,myCanvas.height / 2);
// 小时超过12的减去十二
this.hours = this.hours > 12 ? this.hours - 12 : this.hours;
// 分钟改变对应的小时改变量
this.hour = this.hours + this.minutes / 60;
// 秒钟改变对应的分钟改变量
this.minute = this.minutes + this.seconds / 60;
}
当弄明白这两个难点,参考参考我的代码也能差不多写出来了。
奉上源代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
canvas {
display: block;
margin: 30px auto;
border-radius: 50%;
border: 1px solid #333;
}
</style>
</head>
<body>
<canvas></canvas>
</body>
</html>
<script>
var myCanvas = document.querySelector("canvas");
var ctx = myCanvas.getContext("2d");
// 根据浏览器的宽高来设置canvas的宽高
myCanvas.width = document.documentElement.clientWidth * 0.4;
myCanvas.height = document.documentElement.clientHeight * 0.8;
// 一个时钟类
class Clock{
constructor(){
// 获取一个大约的指针长度
this.BL = myCanvas.width * 0.4;
this.renderNumber();
this.renderCircle();
this.createDate();
this.drawHour();
this.drawMinute();
this.drawSecond();
}
renderNumber(){
// 首先把坐标系移动到圆心
// 记住原始坐标
ctx.save();
// 移动
ctx.translate(myCanvas.width / 2,myCanvas.height / 2);
// 开始绘图
ctx.beginPath();
ctx.fillStyle = `rgb(${~~(Math.random()*255)},${~~(Math.random()*255)},${~~(Math.random()*255)})`;
ctx.fillStyle = "teal";
// 时钟的十二个数字
for(let i = 1;i < 13;i++){
// 每个数字对应的角度
let delta = (i - 3) * (2 * Math.PI) / 12;
//我们需要的指针长度比圆的半径小myCanvas.width / 2 * 0.85
let x = myCanvas.width / 2 * 0.85 * Math.cos(delta);
let y = myCanvas.height / 2 * 0.85 * Math.sin(delta);
ctx.font = "bold 20px Arail";
// 文字水平居中
ctx.textAlign = "center";
// 文字垂直居中
ctx.textBaseline = 'middle';
ctx.fillText(i,x,y);
}
ctx.restore();
}
renderCircle(){
// 记住原始坐标
ctx.save();
// 移动
ctx.translate(myCanvas.width / 2,myCanvas.height / 2);
// 一共六十个小圆,作为秒针的标准
for(let i = 0;i < 60;i++){
ctx.beginPath();
// 每个小圆对应的角度
let delta = (i - 15) * (2 * Math.PI) / 60;
let x = myCanvas.width / 2 * 0.95 * Math.cos(delta);
let y = myCanvas.height / 2 * 0.95 * Math.sin(delta);
// 填充颜色随机~~取整,模板字符串
ctx.fillStyle = `rgb(${~~(Math.random()*255)},${~~(Math.random()*255)},${~~(Math.random()*255)})`;
ctx.arc(x,y,3,2*Math.PI,false);
// 填充
ctx.fill();
}
ctx.restore();
}
// 得到系统的时间
createDate(){
// 得到现在的时间对象
this.date = new Date();
// 分别得到时,分,秒
this.hours = this.date.getHours();
this.minutes = this.date.getMinutes();
this.seconds = this.date.getSeconds();
ctx.font="bold 18px 宋体";
ctx.fillStyle=`rgb(${~~(Math.random()*255)},${~~(Math.random()*255)},${~~(Math.random()*255)})`;
ctx.fillText(`${this.hours}:${this.minutes}:${this.seconds}`,myCanvas.width / 6,myCanvas.height / 2);
// 小时超过12的减去十二
this.hours = this.hours > 12 ? this.hours - 12 : this.hours;
// 分钟改变对应的小时改变量
this.hour = this.hours + this.minutes / 60;
// 秒钟改变对应的分钟改变量
this.minute = this.minutes + this.seconds / 60;
}
// 时针
drawHour(){
// 存储坐标系
ctx.save();
// 移动到大圆的圆心,改变坐标系
ctx.translate(myCanvas.width / 2,myCanvas.height / 2);
// 时间小时的角度
let delta = this.hour * (2 * Math.PI) / 12;
// 指针旋转转动
ctx.rotate(delta);
ctx.fillStyle = `rgb(${~~(Math.random()*255)},${~~(Math.random()*255)},${~~(Math.random()*255)})`;
ctx.beginPath();
// 绘制指针
ctx.moveTo(3,-this.BL * 0.8);
ctx.lineTo(0,-this.BL * 0.81);
ctx.lineTo(-3, -this.BL * 0.8);
ctx.lineTo(-this.BL*0.04, 40);
ctx.lineTo(this.BL*0.04, 40);
ctx.fill();
// 恢复坐标系
ctx.restore();
}
// 分针
drawMinute(){
ctx.save();
// 移动
ctx.translate(myCanvas.width / 2,myCanvas.height / 2);
let delta = this.minute * (2 * Math.PI) / 60;
ctx.rotate(delta);
ctx.fillStyle = `rgb(${~~(Math.random()*255)},${~~(Math.random()*255)},${~~(Math.random()*255)})`;
ctx.beginPath();
ctx.moveTo(2,-this.BL);
ctx.lineTo(0, -this.BL*1.01);
ctx.lineTo(-2, -this.BL);
ctx.lineTo(-this.BL*0.03, 60);
ctx.lineTo(this.BL*0.03, 60);
ctx.fill();
ctx.restore();
}
//秒针
drawSecond(){
ctx.save();
// 移动到大圆的圆心
ctx.translate(myCanvas.width / 2,myCanvas.height / 2);
let delta = this.seconds * (2 * Math.PI) / 60;
ctx.rotate(delta);
ctx.fillStyle = `rgb(${~~(Math.random()*255)},${~~(Math.random()*255)},${~~(Math.random()*255)})`;
ctx.beginPath();
ctx.moveTo(1,-this.BL*1.1);
ctx.lineTo(-1, -this.BL*1.1);
ctx.lineTo(-this.BL*0.02, 80);
ctx.lineTo(this.BL*0.02, 80);
ctx.fill();
ctx.beginPath();
// 绘制一个圆,作为钟表的圆心
ctx.fillStyle ="orange"
ctx.arc(0,0,8,2*Math.PI,false);
// 填充刚绘制的圆心
ctx.fill();
ctx.restore();
}
}
var clock = new Clock();
// 定时器每秒更新
setInterval(()=>{
ctx.clearRect(0,0,myCanvas.width,myCanvas.height);
clock.renderNumber();
clock.renderCircle();
clock.createDate();
clock.drawHour();
clock.drawMinute();
clock.drawSecond();
},1000);
</script>