Javascript HTML5 Canvas 时钟绘制

我这里采用 JavascriptHTML5 Canvas API 来进行 Graphic2D 的绘制,主要利用数学的三角函数与圆有关的公式来达到目的,其实最主要是通过实际练习并对这些数学公式进行深入掌握;也希望能够帮助大伙儿明白其中的原理,我尽量用比较详细的方式进行编写这篇博文。

效果图

IMAGE

数学知识

  1. 圆的弧度与角度的关系,以及圆的基础知识。
    圆周率(π) = 圆周长 / 圆直径(周长与直径的比值);在计算机领域的数学公式计算坐标都是根据弧度为单位进行计算,那我们在计算时就需要把角度转弧度;角度转弧度的根本原理 —— 圆周率就是180°角的弧度,那么角度转弧度公式可以为:角度 / 180° π,而弧度转角度:弧度 / π 180°。

  2. 什么是余弦值与正弦值、正切值。
    IMAGE
    按以上图为例,已知直角三角形AC、AB、BC的长度,想要获取“a”弧度值,咱们可以利用三种方式进行获取,余弦值 —— 对边/斜边(即AB/AC),正弦值 —— 临边/斜边(即BC/AC),正切值 —— 临边/对边(即BC/AB);在数学中如果知道相应的值后可以利用查表的方式得出弧度;计算机中可以利用反函数获取弧度。

相关注意事项: 在计算机中不管是余弦值、正弦值、正切值都有对应的反余弦值、反正弦值、反正切值函数,而函数返回的内容就是咱们需要的弧度/角度值;

计算机Math函数列表

● Math.sin —— 正弦值获取函数
● Math.cos —— 余弦值获取函数
● Math.tan —— 正切值获取函数
● Math.asin —— 反正弦值函数获取弧度
● Math.acos —— 反余弦值函数获取弧度
● Math.atan —— 反正切值函数获取弧度
● Math.atan2 —— 这个函数是计算机特有的升级版反正切函数,应用数学中不存在这种说法,只需要传入一条射线从A点到B点的对应位置即可获取到咱们想要的弧度;如上图:“a”的弧度 = Math.atan2(C坐标,A坐标)。

核心代码

以下相关的代码使用了 JQuery,如果需要运行起来则需要在HTML里面导入JQuery相关的库,然后再把以下语句拷贝至单独JS文件或HTML中。此份代码可以通过我的 Github 地址进行获取:https://github.com/caryyu/clock

画圆与旋转指针

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
Clock = function(){
var jCanvas = $('<canvas width="450" height="300">');
//jCanvas.on('mousemove',onMousemove);
$(document.body).append(jCanvas);
this.canvas = jCanvas[0];
this.ctx = this.canvas.getContext('2d');
this.ctx.strokeStyle = 'red';
this.ctx.fillStyle = 'red';
this.rect = {w:this.canvas.width,h:this.canvas.height,x:0,y:0};
this.radius = 50;
this.count = 0;
this.interval = 100;
};

Clock.prototype = {
drawing : function(){
var radius = this.radius;
var rx = radius;
var ry = radius;
//Core Drawing
this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height);
this.ctx.beginPath();
this.ctx.arc(rx,ry,radius,0,2 * Math.PI);
var radian = (Math.PI * 2 / 12) * this.count;
var x = Math.cos(radian) * radius;
var y = Math.sin(radian) * radius;
this.ctx.moveTo(rx,ry);
this.ctx.lineTo(rx + x , ry + y);
this.ctx.stroke();
//Counting for drawing
this.count++;
if(this.count > 12){this.count = 1}
},
start : function(){
setInterval($.proxy(this.drawing,this),this.interval);
}
};

function globalToLocal(canvas, x, y) {
var bbox = canvas.getBoundingClientRect();
var x = x - bbox.left * (canvas.width / bbox.width);
var y = y - bbox.top * (canvas.height / bbox.height);
return {x: x, y : y};
}


$(function(){
var c = new Clock();
c.start();
});

时钟标记绘制

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
Clock = function(){
var jCanvas = $('<canvas width="450" height="300">');
//jCanvas.on('mousemove',onMousemove);
$(document.body).append(jCanvas);
this.canvas = jCanvas[0];
this.ctx = this.canvas.getContext('2d');
this.ctx.strokeStyle = 'red';
this.ctx.fillStyle = 'red';
this.rect = {w:this.canvas.width,h:this.canvas.height,x:0,y:0};
this.radius = 50;
this.count = 1;
this.interval = 200;
};

Clock.prototype = {
drawingMarks : function(){
var radius = this.radius;
var rx = radius;
var ry = radius;
//Hour Wheel Marks
for(var i=1;i<=12;i++){
var radian = (Math.PI * 2 / 12) * i;
var p1 = this.getPoint(radian,radius - 8);
var p2 = this.getPoint(radian,radius);
this.ctx.moveTo(rx + p1.x , ry + p1.y);
this.ctx.lineTo(rx + p2.x , ry + p2.y);
}
//Minute Wheel Marks
for(var j=1;j<=60;j++){
var radian = (Math.PI * 2 / 60) * j;
var p1 = this.getPoint(radian,radius - 3);
var p2 = this.getPoint(radian,radius);
this.ctx.moveTo(rx + p1.x , ry + p1.y);
this.ctx.lineTo(rx + p2.x , ry + p2.y);
}
},
drawing : function(){
var radius = this.radius;
var rx = radius;
var ry = radius;
//Core Drawing
this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height);
this.ctx.beginPath();
this.ctx.arc(rx,ry,radius,0,2 * Math.PI);
var radian = (Math.PI * 2 / 12) * this.count;
var p = this.getPoint(radian,radius);
this.drawingMarks();
this.ctx.moveTo(rx, ry);
this.ctx.lineTo(rx + p.x , ry + p.y);
this.ctx.stroke();
//Counting for drawing
this.count++;
if(this.count > 12){this.count = 1}
},
getPoint : function(radian,radius){
var x = Math.cos(radian) * radius;
var y = Math.sin(radian) * radius;
return {x : x , y : y};
},
start : function(){
//this.drawing();
setInterval($.proxy(this.drawing,this),this.interval);
}
};

function globalToLocal(canvas, x, y) {
var bbox = canvas.getBoundingClientRect();
var x = x - bbox.left * (canvas.width / bbox.width);
var y = y - bbox.top * (canvas.height / bbox.height);
return {x: x, y : y};
}


$(function(){
var c = new Clock();
c.start();
});

时钟时分秒针绘制

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
Clock = function(){
var jCanvas = $('<canvas width="450" height="300">');
//jCanvas.on('mousemove',onMousemove);
$(document.body).append(jCanvas);
this.canvas = jCanvas[0];
this.ctx = this.canvas.getContext('2d');
this.ctx.strokeStyle = 'red';
this.ctx.fillStyle = 'red';
this.rect = {w:this.canvas.width,h:this.canvas.height,x:0,y:0};
this.radius = 50;
this.count = 1;
this.interval = 1000;
};

Clock.prototype = {
drawingMarks : function(){
var radius = this.radius;
var rx = radius;
var ry = radius;
//Hour Wheel Marks
for(var i=1;i<=12;i++){
var radian = (Math.PI * 2 / 12) * i;
var p1 = this.getPoint(radian,radius - 8);
var p2 = this.getPoint(radian,radius);
this.ctx.moveTo(rx + p1.x , ry + p1.y);
this.ctx.lineTo(rx + p2.x , ry + p2.y);
}
//Minute Wheel Marks
for(var j=1;j<=60;j++){
var radian = (Math.PI * 2 / 60) * j;
var p1 = this.getPoint(radian,radius - 3);
var p2 = this.getPoint(radian,radius);
this.ctx.moveTo(rx + p1.x , ry + p1.y);
this.ctx.lineTo(rx + p2.x , ry + p2.y);
}
},
drawing : function(){
var radius = this.radius;
var rx = radius;
var ry = radius;
//Core Drawing
this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height);
this.ctx.beginPath();
this.ctx.arc(rx,ry,radius,0,2 * Math.PI);
var radian = (Math.PI * 2 / 12) * this.count;
var p = this.getPoint(radian,radius);
this.drawingMarks();
//
this.ctx.moveTo(rx, ry);
this.ctx.arc(rx,ry,2,0,Math.PI*2);
//
var date = new Date();
//Hour Wheel
var hours = date.getHours();
radian = (Math.PI * 2 / 12) * (hours - 12);
var p1 = this.getPoint(radian - Math.PI/2,radius-25);
this.ctx.moveTo(rx, ry);
this.ctx.lineTo(rx + p1.x , ry + p1.y);
//Minute Wheel
var minutes = date.getMinutes();
radian = (Math.PI * 2 / 60) * minutes;
var p2 = this.getPoint(radian - Math.PI/2,radius-15);
this.ctx.moveTo(rx, ry);
this.ctx.lineTo(rx + p2.x , ry + p2.y);
//Second Wheel
var seconds = date.getSeconds();
radian = (Math.PI * 2 / 60) * seconds;
var p3 = this.getPoint(radian - Math.PI/2,radius);
this.ctx.moveTo(rx, ry);
this.ctx.lineTo(rx + p3.x , ry + p3.y);
this.ctx.stroke();
},
getPoint : function(radian,radius){
var x = Math.cos(radian) * radius;
var y = Math.sin(radian) * radius;
return {x : x , y : y};
},
start : function(){
//this.drawing();
setInterval($.proxy(this.drawing,this),this.interval);
}
};

function globalToLocal(canvas, x, y) {
var bbox = canvas.getBoundingClientRect();
var x = x - bbox.left * (canvas.width / bbox.width);
var y = y - bbox.top * (canvas.height / bbox.height);
return {x: x, y : y};
}


$(function(){
var c = new Clock();
c.start();
});

完整版

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
Clock = function(){
var jCanvas = $('<canvas width="450" height="300">');
//jCanvas.on('mousemove',onMousemove);
$(document.body).append(jCanvas);
this.canvas = jCanvas[0];
this.ctx = this.canvas.getContext('2d');
this.ctx.strokeStyle = 'red';
this.ctx.fillStyle = 'red';
this.rect = {w:this.canvas.width,h:this.canvas.height,x:0,y:0};
this.radius = 50;
this.count = 1;
this.interval = 1000;
};

Clock.prototype = {
drawingMarks : function(){
var radius = this.radius;
var rx = radius;
var ry = radius;
//Hour Wheel Marks
for(var i=1;i<=12;i++){
var radian = (Math.PI * 2 / 12) * i;
var p1 = this.getPoint(radian,radius - 8);
var p2 = this.getPoint(radian,radius);
this.ctx.moveTo(rx + p1.x , ry + p1.y);
this.ctx.lineTo(rx + p2.x , ry + p2.y);
}
//Minute Wheel Marks
for(var j=1;j<=60;j++){
var radian = (Math.PI * 2 / 60) * j;
var p1 = this.getPoint(radian,radius - 3);
var p2 = this.getPoint(radian,radius);
this.ctx.moveTo(rx + p1.x , ry + p1.y);
this.ctx.lineTo(rx + p2.x , ry + p2.y);
}
},
drawing : function(){
var radius = this.radius;
var rx = radius;
var ry = radius;
//Core Drawing
this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height);
this.ctx.beginPath();
this.ctx.arc(rx,ry,radius,0,2 * Math.PI);
var radian = (Math.PI * 2 / 12) * this.count;
var p = this.getPoint(radian,radius);
this.drawingMarks();
//
this.ctx.moveTo(rx, ry);
this.ctx.arc(rx,ry,2,0,Math.PI*2);
//
var date = new Date();
var hours = date.getHours();
var minutes = date.getMinutes();
var seconds = date.getSeconds();
//Hour Wheel
radian = (Math.PI * 2 / 12) * (hours - 12) + (Math.PI * 2 / 12) * (minutes / 60);
var p1 = this.getPoint(radian - Math.PI/2,radius-25);
this.ctx.moveTo(rx, ry);
this.ctx.lineTo(rx + p1.x , ry + p1.y);
//Minute Wheel
radian = (Math.PI * 2 / 60) * minutes + (Math.PI * 2 / 12 / 60 / 60) * seconds;;
var p2 = this.getPoint(radian - Math.PI/2,radius-15);
this.ctx.moveTo(rx, ry);
this.ctx.lineTo(rx + p2.x , ry + p2.y);
//Second Wheel
radian = (Math.PI * 2 / 60) * seconds;
var p3 = this.getPoint(radian - Math.PI/2,radius);
this.ctx.moveTo(rx, ry);
this.ctx.lineTo(rx + p3.x , ry + p3.y);
this.ctx.stroke();
},
getPoint : function(radian,radius){
var x = Math.cos(radian) * radius;
var y = Math.sin(radian) * radius;
return {x : x , y : y};
},
start : function(){
//this.drawing();
setInterval($.proxy(this.drawing,this),this.interval);
}
};

function globalToLocal(canvas, x, y) {
var bbox = canvas.getBoundingClientRect();
var x = x - bbox.left * (canvas.width / bbox.width);
var y = y - bbox.top * (canvas.height / bbox.height);
return {x: x, y : y};
}


$(function(){
var c = new Clock();
c.start();
});

SEO

如何/怎么 截图 画箭头实现,类似 QQ 那种截图后画箭头的功能。
如何/怎么 画一个闹钟展现在页面/界面上。

最后

以上这些是我个人总结的一些肤浅的知识,如果有不对的地方还望多多赐教,谢谢。