前言:写在前面的话
什么是表盘的时钟,先来看一张动图
先来,说一下这个图吧。这个图的所有的部分,都是我自己画出来的,除了后面的背景图片是网上找的素材。这个时钟,①可以进行大小的任意缩放,完全可以加入到自己的页面中,作为一个小的控件。②可以创建任意多个,也就是所,采用了面向对象的方式,画出来的。
先来说一下写这个钟的遇到的几个难点:
- 如何画出刻度:这里用到了
canva
里面的坐标平移translate();
和坐标旋转方法rotate()
,当然,为了比必要的麻烦,也用了save();
和restore();
来进行状态的保存和恢复。下面来看一幅图
看到这个图,下面画出刻度就非常容易了。只需要调用moveTo();
和lineTo():
当然还有stroke();
方法,按照当前外圆半径的一定比例进行画在x
轴上的直线即可。
到现在为止,一个刻度画完了。那么其他刻度呢?其实,也很好办。只需要每次旋转坐标轴一定角度,然后,重复调用上面的方法即可。那么旋转多少度呢。60(小格) = 360度
,即2*Math.PI/60
.
当然还可以做得漂亮一点,就是每一个整点相应的刻度会比其他刻度长一点,这个其实很简单就是
i%5==0
如果条件成立,就画的长一点,否则,不进行长度的调整。 - 如何画刻度上面的数字。可以看到这些数字都是垂直的所以不能按照上面的坐标旋转的方法进行画取。那么如何画呢?先来一张图把。
只需要按照这个方法(每次旋转30度)就可以迭代的求出所有的[1, 12]的坐标点 - 时针如何画:起始这里有一个注意点,就是后画的图形会覆盖先画的图形。所以这里,我采用的是:时针、分针、秒针的顺序画的
时针:一共要花两个部分:①轴承②指针,轴承很容易,指针也比较简单(注意:这里的坐标系以第一张图的初始坐标系为准)指针,就是按照圆半径的一定比例求得各自的长度,然后在按照各自长度的比例(这里,我采用的是1:3,后面1,前面3)的方式,画出一条直线。
经过上面几步,所以的东西都画完了。(背景图可选)
- 那么如何显示静态时间呢:首先获取当前系统的时间:
date.getHours();
、date.getMinutes();
、date.getSeconds();
注意这里有获取准确的时分秒,???,什么是准确的呢,就是像,分的获取必须要加上秒的值,时的获取必须要加上分和秒的值(注意:时分秒的进制不同,要进行换算,还有,一个注意点,就是小时24进制要转换为12进制)。
好了得到了当前的时分秒,如何映射到,表盘的时针上呢,也简单。就是调用rotate();
函数旋转坐标系,当然,每一个时针都要进行旋转角度(注意:状态的保存和恢复save()
和restore()
)
下面就是旋转角度的计算:- 时:
hour * 2* Math.PI / 12
- 分:
minurte * 2 * Math.PI / 60
- 秒:
second * 2 * Math.PI / 60
静态时间显示完成,动态时间也就简单了
- 时:
- 显示动态时间:调用一个定时器
setInterval();
①:清除上次画的像素clearRect(x, y, w, h);
②:显示静态静态时间
第一部分:核心类DialClock
1 属性
this.size = size;
// 大小this.img = null;
// 背景图片this.frameWidth = this.size / 24;
// 框架宽度this.scaleLineWidth = this.frameWidth / 5;
// 刻度线宽this.outerR = this.size / 2 - this.frameWidth;
// 表盘半径this.innerR = this.outerR * 2 / 3;
// 内圆半径this.fontSize = this.outerR / 8;
// 字体大小this.bearingR = [this.outerR / 4, this.outerR / 8, this.outerR / 16];
// 轴承半径this.bearingColor = [hourColor, minuteColor, secondColor];
// 轴承颜色this.centerPoint = [this.size / 2, this.size / 2];
// 圆心this.box = box;
this.scalePoint = [this.outerR / 6 * 5, this.outerR - this.frameWidth - 2];
// 刻度坐标this.timeId = null;
// 计时id
2 方法
init
: 初始化方法,主要是canvas
的创建ctx
的获取,以及可选的img
背景图片的创建drawFrame
: 画出表盘的边框drawLine
: 画出刻度线drawScale
: 画出刻度包括刻度线和刻度文字,调用了drawLine
方法drawBearingAndTimeHand
: 画轴承和时针drawDial
: 画出表盘,包括边框和刻度,注意这里不画轴承和时针,可选画背景图rotateByTime
: 根据当前时间,旋转坐标,画出轴承和时针showStaticTime
: 显示当前的静态时间showDynamicTime
: 显示动态时间stopShowTime
: 停止显示时间
第二部分:原代码
dialclock.js
/** 表盘时钟 **/
function DialClock(box, size = 500, imgPath = "../../images/1.jpg", hourColor = "#233", minuteColor = "green", secondColor = "red") {
this.size = size; // 大小
this.img = null; // 背景图片
this.frameWidth = this.size / 24; // 框架宽度
this.scaleLineWidth = this.frameWidth / 5; // 刻度线宽
this.outerR = this.size / 2 - this.frameWidth; // 表盘半径
this.innerR = this.outerR * 2 / 3; // 内圆半径
this.fontSize = this.outerR / 8; // 字体大小
this.bearingR = [this.outerR / 4, this.outerR / 8, this.outerR / 16]; // 轴承半径
this.bearingColor = [hourColor, minuteColor, secondColor]; // 轴承颜色
this.centerPoint = [this.size / 2, this.size / 2]; // 圆心
this.box = box;
this.scalePoint = [this.outerR / 6 * 5, this.outerR - this.frameWidth - 2]; // 刻度坐标
this.timeId = null; // 计时id
// 完成初始化工作
this.init = function () {
// 获取canvas
this.canvas = document.createElement("canvas");
// 设置canvas 宽度和高度
this.canvas.width = this.canvas.height = this.size;
// 将canvas挂载到容器里
this.box.appendChild(this.canvas);
// 获取 ctx
this.ctx = this.canvas.getContext("2d");
// 设置字体
this.ctx.font = this.fontSize + "px sans-serif";
// 创建背景图片
if (imgPath) {
this.img = document.createElement("img");
this.img.src = imgPath;
}
}
// 画框架
this.drawFrame = function (r, width) {
this.ctx.beginPath();
this.ctx.arc(this.centerPoint[0], this.centerPoint[1], r, 0, 2 * Math.PI);
this.ctx.closePath();
this.ctx.lineWidth = width;
// 添加线性渐变
var grd = this.ctx.createLinearGradient(0, 0, this.size, this.size);
grd.addColorStop(0, "#CFB53B");
grd.addColorStop(1, "black");
// 设置线条颜色为渐变色
this.ctx.strokeStyle = grd;
this.ctx.stroke();
};
// 画线
this.drawLine = function () {
// 状态保存
this.ctx.save();
// ---------------------------------- 设置初始坐标系 ----------------------------------
// 用来移动 canvas 的原点到指定的位置
this.ctx.translate(this.centerPoint[0], this.centerPoint[1]);
// 旋转坐标轴
this.ctx.rotate(-Math.PI / 2);
// 设置线条末端样式
this.ctx.lineCap = "round";
this.ctx.lineWidth = this.scaleLineWidth;
for (var i = 0; i < 60; i++) {
this.ctx.beginPath();
// 设置起点
if (i % 5 == 0) {
this.ctx.strokeStyle = "#000";
this.ctx.moveTo(this.scalePoint[0] - (this.scalePoint[1] - this.scalePoint[0]) / 2, 0);
}
else {
this.ctx.strokeStyle = "#555";
this.ctx.moveTo(this.scalePoint[0], 0);
}
// 设置终点
this.ctx.lineTo(this.scalePoint[1], 0);
// 旋转坐标轴
this.ctx.rotate(2 * Math.PI / 60);
this.ctx.stroke();
}
// 状态恢复
this.ctx.restore();
}
// 画刻度
this.drawScale = function () {
// 1. 画线
this.drawLine();
// 2. 画文字
for (var i = 1; i <= 12; i++) {
var x = this.centerPoint[0] + this.innerR * Math.sin(Math.PI / 6 * i) - this.scaleLineWidth;
var y = this.centerPoint[1] - this.innerR * Math.cos(Math.PI / 6 * i) + this.scaleLineWidth;
this.ctx.fillText(i.toString(), x, y);
}
}
// 画轴承和时针
this.drawBearingAndTimeHand = function (timeHandType) {
// 画轴承
this.ctx.beginPath();
this.ctx.arc(0, 0, this.bearingR[timeHandType], 0, 2 * Math.PI);
this.ctx.closePath();
this.ctx.fillStyle = this.bearingColor[timeHandType];
this.ctx.fill();
// 画时针
if (timeHandType == 2) {
var timeHandLen = this.innerR + this.fontSize * 4;
this.ctx.lineWidth = this.scaleLineWidth;
this.ctx.strokeStyle = this.bearingColor[2];
} else if (timeHandType == 1) {
var timeHandLen = this.innerR + this.fontSize;
this.ctx.lineWidth = this.scaleLineWidth * 2;
this.ctx.strokeStyle = this.bearingColor[1];
} else if (timeHandType == 0) {
var timeHandLen = this.innerR;
this.ctx.lineWidth = this.scaleLineWidth * 3;
this.ctx.strokeStyle = this.bearingColor[0];
}
// 设置线条末端样式
this.ctx.lineCap = "round";
this.ctx.beginPath();
this.ctx.moveTo(-timeHandLen / 4, 0);
this.ctx.lineTo(timeHandLen / 4 * 3, 0);
this.ctx.stroke();
};
// 画表盘
this.drawDial = () => {
// --------------------------- 绘制背景图 ---------------------------
if (this.img) {
this.ctx.drawImage(this.img, this.centerPoint[0]-this.img.width/2, this.centerPoint[1]-this.img.height/2);
// this.ctx.drawImage(this.img, 0, 0);
}
// --------------------------- 绘制框架 ---------------------------
this.drawFrame(this.outerR, this.frameWidth);
// --------------------------- 绘制刻度 ---------------------------
this.drawScale();
// this.drawFrame(this.innerR, 10);
// --------------------------- 绘制轴时针 ---------------------------
// this.drawBearingAndTimeHand(0);
// this.drawBearingAndTimeHand(1);
// this.drawBearingAndTimeHand(2);
};
// 根据当前时间,旋转坐标,画出轴承和时针
this.rotateByTime = function (timeType, rotate) {
this.ctx.save();
this.ctx.rotate(rotate);
this.drawBearingAndTimeHand(timeType);
this.ctx.restore();
};
// 显示静态的当前时间
this.showStaticTime = () => {
// 清除表盘像素
this.ctx.clearRect(0, 0, this.size, this.size);
// 画表盘
this.drawDial();
// 获取当前时间
var date = new Date();
var second = date.getSeconds();
var minute = date.getMinutes() + second / 60;
var hour = date.getHours() % 12 + minute / 60; // 24进制转换为12进制
var timeRotate = [hour * 2 * Math.PI / 12, minute * 2 * Math.PI / 60, second * 2 * Math.PI / 60];
// 保存状态
this.ctx.save();
// -------------------- 更改坐标系到圆点 --------------------
this.ctx.translate(this.centerPoint[0], this.centerPoint[1]);
this.ctx.rotate(-Math.PI / 2);
// -------------------- 旋转坐标系 --------------------
for (var i = 0; i < 3; i++) {
this.rotateByTime(i, timeRotate[i]);
}
// 恢复状态
this.ctx.restore();
};
// 显示动态时间
this.showDynamicTime = function () {
this.showStaticTime()
this.timeId = setInterval(this.showStaticTime, 1000);
};
// 停止显示时间
this.stopShowTime = function () {
clearInterval(this.timeId);
}
// 主要逻辑
this.run = function () {
// 1. 初始化
this.init();
// 2. 显示时间
this.showDynamicTime();
};
}
·
表盘时针.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>04-表盘时钟</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
</style>
</head>
<script src="../dialclock.js"></script>
<body>
<div class="box"></div>
</body>
<script>
var box = document.getElementsByClassName("box")[0];
var dialClock = new DialClock(box);
dialClock.run();
</script>
</html>