canvas制作钟表

之前用html+css+JavaScript实现了一个简单钟表,但还是有一些问题,主要是一些css属性不同浏览器支持效果不一样,所以尝试用 canvas实现了一个简单的钟表,效果在下方,当然了,采用canvas同样会有一些浏览器不支持。。。 这里只讨论canvas的实现方式。^_^

html部分

html部分很简单,写入canvas标签,其id设置为“canvas”,用css设置成居中显示,代码如下:

复制代码
 1<!doctype html> 2<html> 3<head> 4<meta charset='utf-8'> 5<style> 6    canvas{display:block;margin:5px auto;} 7</style> 8<title>clock</title> 9</head>10<body>11<canvas id='canvas'>您的浏览器不支持canvas标签</canvas>12<script type='text/javascript' src="外部JavaScript文件路径"></script>13</body>14</html>
复制代码

备注:需要通过script标签的src属性引入外部JavaScript文件

JavaScript部分

可以通过js获取Canvas对象,设置画布的宽高,不过绘图工作是通过操作CanvasRenderingContext2D对象来操作的,获取此对象的方法:

1var canvas = document.getElementById('canvas');    // Canvas对象2 canvas.width = 450;        //3 canvas.height = 450;    //4var context = canvas.getContext('2d'); // 获取绘图上下文环境

获取到绘图上下文context以后即可绘制钟表了,整体思路是这样的:定义一个全局变量记录当前系统时间信息,用setInterval方法重复执行,获取时间信息,并绘制图像,代码如下:

1var currentDateObj; // 记录当前时间信息2 setInterval(function(){
3     update(); // 修改currentDateObj的内容4     draw(); // 根据currentDateObj的内容绘制图像5 }, 500);

本文实现重点在于绘制图像,关于update方法的实现,可以直接查看后边的整体js代码,draw方法中,主要涉及到的一些内容有:线段和圆的绘制、canvas文字渲染、canvas阴影实现、canvas图形变换等内容

canvas绘制线段、圆

绘制线段很简单,只需调用moveTo、lineTo方法设置线段路径,调用stroke方法完成绘制

复制代码
 1 context.moveTo(20, 70);
 2 context.lineTo(140, 70);
 3 context.lineTo(140, 40);
 4 context.lineTo(180, 80);
 5 context.lineTo(140, 120);
 6 context.lineTo(140, 90);
 7 context.lineTo(20, 90);
 8 context.lineWidth = 2; // 设置线段宽度 9 context.strokeStyle = "#058"; // 设置颜色10 context.stroke();    // 绘制到画布上
复制代码

运行结果:

绘制圆需要调用arc方法,然后调用stroke完成绘制,arc有5个参数,分别表示圆弧圆心的x坐标、y坐标、圆弧半径、起始角、结束角,最后一个参数是一个布尔值,false表示顺时针,true表示逆时针,下面是一些示例

复制代码
 1for(var i = 0; i < 2; i++) {
 2for(var j = 0; j < 4; j++){
 3var b = (i == 0) ? false : true;
 4        context.beginPath();
 5         context.lineWidth = 2;
 6         context.strokeStyle = '#058';
 7         context.arc(150 * j + 75, 150 * i + 75, 45, 0, (j + 1) * Math.PI / 2, b);
 8        context.stroke();
 9    }
10 }
复制代码

运行结果:

通过moveTo、lineTo或者arc方法设置好图形路径以后,调用stroke方法完成图形绘制,如果需要绘制一个区域,需要调用fill方法,如上例中,可以将stroke改成fill代码如下:

复制代码
1for(var i = 0; i < 2; i++) {
2for(var j = 0; j < 4; j++){
3var b = (i == 0) ? false : true;
4        context.beginPath();
5         context.fillStyle = '#058';
6         context.arc(150 * j + 75, 150 * i + 75, 45, 0, (j + 1) * Math.PI / 2, b);
7        context.fill();
8    }
9 }
复制代码

运行结果如下:

canvas文字渲染

在canvas画布上写段文字很简单,基本分两步:1.通过设置context的font属性设置文本的样式;2. 调用fillText或strokeText方法渲染文字

1 context.font = "bold 40px Arial";
2 context.fillStyle = '#058';
3 context.fillText("Canvas制作简单钟表", 50, 50);
4 context.strokeStyle = '#058';
5 context.strokeText("Canvas制作简单钟表", 50, 150);

运行结果如下:

可以设置textAlign和textBaseline属性分别设置文本水平和垂直方向的对齐方式

canvas阴影效果

canvas设置阴影主要涉及到的属性有:shadowColor设置阴影颜色,shadowOffsetX和shadowOffsetY分别设置阴影在x和y方向上的偏移量,shadowBlur设置阴影的模糊程度

1 context.shadowColor = "#444";    // 阴影颜色2 context.shadowOffsetX = 10;    // 阴影在x方向偏移量 负数为反方向3 context.shadowOffsetY = 10; // 阴影在y方向偏移量 负数为反方向4 context.shadowBlur = 10;    // 阴影模糊程度5 context.fillStyle = "#058";
6 context.fillRect(100, 50, 300, 100);

运行结果如下:

canvas图形变换

canvas中最基本的图形变换涉及到的方法是:位移 translate(x, y),旋转 rotate(deg),缩放 scale(sx, sy),我们知道默认的坐标原点(0, 0)在canvas的左上角,但是对于我们的钟表来说,如果能将坐标原点移动到表盘的圆心位置,处理起来将很方便,rotate将图像旋转制定的角度,旋 转中心点即坐标原点,scale将图像按制定的比例在x和y方向上进行缩放。需要注意的是,cavnas是基于状态的,如果通过调用 translate(100, 100)方法,将坐标原点移动到(100, 100)的位置以后,如果再次调用,那么坐标原点将在当前基础上累加,所以在实际调用时,需要掉用save方法保存当前状态,完成绘制以后调用 restore方法还原

本例中多次应用translate方法,设置表盘圆心为坐标原点,用rotate方法旋转图像,如刻度的绘制,只是绘制水平方向的一条线段,通过旋转不同角度来达到效果

clearRect方法

clearRect方法用于清空一个矩形空间站的图像,语法为:context.clearRect(x, y, width, height); 第一个参数x 表示要清除的矩形左上角的x坐标,第二个参数y 表示要清除的矩形左上角的y坐标,width参数表示要清除的矩形的宽度,height参数表示要清除的矩形的高度。

在本例中,draw方法的第一步就是调用此方法,清空整个canvas画布的内容,然后才根据currentDateObj中的内容绘制图像。

JavaScript部分的完整代码

准备工作基本做完了,下面是本例中完整的JavaScript代码:

复制代码
  1 (function(){
  2var config = {
  3        canvas : {
  4             width : 420, // 设置canvas的宽  5             height : 420, // 设置canvas的高      6        },
  7        clock : {
  8             radius : 200, // 设置表盘半径  9             borderWidth : 10, // 表盘边框宽度 10            origin : {
 11                 radius : 8, // 中心点 半径 12                 color : '#333' // 中心点 颜色 13            },
 14            hand : {
 15                 hour : {width : 5, length : 80}, // 时针宽度和长度 16                 minute : {width : 2, length : 110}, // 分针的宽度和长度 17                 second : {length : 160} // 秒针长度 18            }
 19        }
 20    };
 21 22var canvas = document.getElementById('canvas');
 23     canvas.width = config.canvas.width;
 24     canvas.height = config.canvas.height;
 25var context = canvas.getContext('2d');
 26 27var currentDateObj;    // 保存当前时间 28 29     setInterval(function(){
 30        update();
 31        draw();
 32     }, 500);
 33 34function update(){
 35         currentDateObj = getDateObj();
 36 37function getDateObj() {
 38var week = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
 39var d = new Date();
 40var year = d.getFullYear();
 41var month = d.getMonth() + 1;
 42var date = d.getDate();
 43var day = d.getDay();
 44var hour = d.getHours();
 45var minute = d.getMinutes();
 46var second = d.getSeconds();
 47             month = (month < 9) ? '0' + month : '' + month;
 48             date = (date < 9) ? '0' + date : '' + date;
 49             hour = (hour < 9) ? '0' + hour : '' + hour;
 50             minute = (minute < 9) ? '0' + minute : '' + minute;
 51             second = (second < 9) ? '0' + second : '' + second;
 52var time = [hour, minute, second];
 53var str = year + '-' + month + '-' + date + ' ' + week[day];
 54return {
 55                dateStr: str,
 56                dateTime: time
 57            }
 58        }
 59    }
 60 61function draw() {
 62         context.clearRect(0, 0, canvas.width, canvas.height);
 63 64        drawBorder();
 65        drawScale();
 66        drawNumbers();
 67        drawTime();
 68        drawHand();
 69        drawOrigin();
 70 71/**
 72         * 绘制表盘边框
 73*/ 74function drawBorder() {
 75            context.save();
 76            context.beginPath();
 77             context.arc(canvas.width / 2, canvas.height / 2, config.clock.radius, 0, 2 * Math.PI, false);
 78             context.arc(canvas.width / 2, canvas.height / 2, config.clock.radius - config.clock.borderWidth, 0, 2 * Math.PI, true);
 79             context.fillStyle = "#333";
 80             context.shadowColor = "#444";
 81             context.shadowBlur = 10;
 82            context.closePath();
 83            context.fill();
 84            context.restore();
 85        }
 86 87/**
 88         * 绘制中心圆点
 89*/ 90function drawOrigin() {
 91            context.save();
 92            context.beginPath();
 93             context.arc(canvas.width / 2, canvas.height / 2, config.clock.origin.radius, 0, 2 * Math.PI, false);
 94             context.fillStyle = config.clock.origin.color;
 95             context.shadowColor = "#444";
 96             context.shadowBlur = 3;
 97            context.closePath();
 98            context.fill();
 99            context.restore();
100        }
101102/**
103         * 绘制表盘刻度
104*/105function drawScale() {
106for(var i = 0; i < 60; i++) {
107var obj = {
108                     sx : config.clock.radius - 15 - config.clock.borderWidth,
109                     sy : 0,
110                     ex : config.clock.radius - config.clock.borderWidth - 5,
111                     ey : 0,
112                     color : "#333",
113                     width : 1
114                };
115                context.save();
116                 context.translate(canvas.width / 2, canvas.height / 2);
117                 context.rotate(i * 6 * Math.PI / 180);
118                context.beginPath();
119if(i % 5 == 0) {
120                     obj.width = 3;
121                     obj.color = "#000";
122                }
123if(i % 15 == 0) {
124                     obj.sx = config.clock.radius - 20 - config.clock.borderWidth;
125                }
126                context.moveTo(obj.sx, obj.sy);
127                context.lineTo(obj.ex, obj.ey);
128                 context.strokeStyle = obj.color;
129                 context.lineWidth = obj.width;
130                context.closePath();
131                context.stroke();
132                context.restore();
133            }
134        }
135136/**
137         * 获取1-12数字
138*/139function drawNumbers() {
140var radius = config.clock.radius - config.clock.borderWidth - 40;
141for(var i = 0; i < 12; i++) {
142                context.save();
143                context.beginPath();
144                 context.translate(canvas.width / 2, canvas.height / 2);
145                 context.font = "normal 28px Arial";
146if((i + 1) % 3 == 0) {
147                     context.font = "normal 36px Arial";
148                }
149                 context.textAlign = 'center';
150                 context.textBaseline = 'middle';
151                 context.fillText(i + 1, -radius * Math.cos((-i * 30 - 120) * Math.PI  / 180), radius * Math.sin((-i * 30 - 120) * Math.PI / 180));
152                context.closePath();
153                context.restore();
154            }
155        }
156157/**
158         * 绘制下方的文字
159*/160function drawTime() {
161            context.save();
162             context.translate(canvas.width / 2, canvas.height / 2);
163            drawDateStr();
164            drawTimeBox();
165            drawTime();
166            context.restore();
167168/**
169             * 绘制 年月日星期信息
170*/171function drawDateStr() {
172                context.beginPath();
173                 context.font = "bold 14px Arial";
174                 context.textAlign = 'center';
175                 context.textBaseline = 'middle';
176                 context.shadowColor = '#ccc';
177                 context.shadowBlur = 2;
178                context.closePath();
179                 context.fillText(currentDateObj.dateStr, 0, 40);
180            }
181182/**
183             * 绘制显示时分秒的背景盒子
184*/185function drawTimeBox() {
186                context.beginPath();
187                 context.fillStyle = "#555";
188                 context.shadowColor = "#444";
189                 context.shadowBlur = 3;
190                context.closePath();
191                 context.fillRect(-47, 60, 30, 30);
192                 context.fillRect(-15, 60, 30, 30);
193                 context.fillRect(17, 60, 30, 30);
194            }
195196/**
197             * 绘制时分秒数字
198*/199function drawTime() {
200                context.beginPath();
201                 context.font = "normal 14px Arial";
202                 context.textAlign = 'center';
203                 context.textBaseline = 'middle';
204                 context.fillStyle = "#fff";
205                context.closePath();
206                 context.fillText(currentDateObj.dateTime[0], -32, 75);
207                 context.fillText(currentDateObj.dateTime[1], 0, 75);
208                 context.fillText(currentDateObj.dateTime[2], 32, 75);
209            }
210211        }
212213/**
214         * 绘制时分秒针
215*/216function drawHand() {
217218var _hour = currentDateObj.dateTime[0] % 12;
219var _minute = currentDateObj.dateTime[1];
220var _second = currentDateObj.dateTime[2];
221            drawHourHand();
222            drawMinuteHand();
223            drawSecondHand();
224225// 时针226function drawHourHand() {
227                context.save();
228                 context.translate(canvas.width / 2, canvas.height / 2);
229                 context.rotate(((_hour + _minute / 60) - 3) * 30 * Math.PI / 180);
230                context.beginPath();
231                 context.moveTo(-12, 0);
232                 context.lineTo(config.clock.hand.hour.length, 0);
233                 context.lineWidth = config.clock.hand.hour.width;
234                 context.strokeStyle = config.clock.origin.color;
235                 context.lineCap = "round";
236                 context.shadowColor = "#999";
237                 context.shadowBlur = 5;
238                 context.shadowOffsetX = 5;
239                 context.shadowOffsetY = 5;
240                context.stroke();
241                context.stroke();
242                context.closePath();
243                context.restore();
244            }
245246// 分针247function drawMinuteHand() {
248                context.save();
249                 context.translate(canvas.width / 2, canvas.height / 2);
250                 context.rotate((_minute - 15) * 6 * Math.PI / 180);
251                context.beginPath();
252                 context.moveTo(-18, 0);
253                 context.lineTo(config.clock.hand.minute.length, 0);
254                 context.lineWidth = config.clock.hand.minute.width;
255                 context.strokeStyle = config.clock.origin.color;
256                 context.lineCap = "round";
257                 context.shadowColor = "#999";
258                 context.shadowBlur = 5;
259                 context.shadowOffsetX = 5;
260                 context.shadowOffsetY = 5;
261                context.stroke();
262                context.closePath();
263                context.restore();
264            }
265266// 秒针267function drawSecondHand() {
268                context.save();
269                 context.translate(canvas.width / 2, canvas.height / 2);
270                 context.rotate((_second - 15) * 6 * Math.PI / 180);
271                context.beginPath();
272                 context.moveTo(-35, 1.5);
273                 context.lineTo(0, 1.5);
274                 context.lineTo(config.clock.hand.second.length, 0.5);
275                 context.lineTo(config.clock.hand.second.length, -0.5);
276                 context.lineTo(0, -1.5);
277                 context.lineTo(-35, -1.5);
278                context.closePath();
279                 context.fillStyle = "#f60";
280                 context.shadowColor = "#999";
281                 context.shadowBlur = 5;
282                 context.shadowOffsetX = 5;
283                 context.shadowOffsetY = 5;
284                context.fill();
285                context.restore();
286            }
287        }
288    }
289 }());

好的,以下是使用Python的tkinter模块和canvas组件制作动态钟表的代码: ``` import tkinter as tk import time class Clock: def __init__(self, canvas, x, y, radius): self.canvas = canvas self.x = x self.y = y self.radius = radius self.hour_hand = None self.minute_hand = None self.second_hand = None def draw_clock_face(self): self.canvas.create_oval(self.x - self.radius, self.y - self.radius, self.x + self.radius, self.y + self.radius, width=2) def draw_hour_marks(self): for i in range(12): x1 = self.x + (self.radius - 10) * math.cos(math.pi / 6 * i - math.pi / 2) y1 = self.y + (self.radius - 10) * math.sin(math.pi / 6 * i - math.pi / 2) x2 = self.x + (self.radius - 20) * math.cos(math.pi / 6 * i - math.pi / 2) y2 = self.y + (self.radius - 20) * math.sin(math.pi / 6 * i - math.pi / 2) self.canvas.create_line(x1, y1, x2, y2, width=2) def draw_hour_hand(self, hour): length = self.radius * 0.5 angle = math.pi / 6 * (hour % 12) - math.pi / 2 x = self.x + length * math.cos(angle) y = self.y + length * math.sin(angle) if self.hour_hand: self.canvas.coords(self.hour_hand, self.x, self.y, x, y) else: self.hour_hand = self.canvas.create_line(self.x, self.y, x, y, width=4) def draw_minute_hand(self, minute): length = self.radius * 0.8 angle = math.pi / 30 * minute - math.pi / 2 x = self.x + length * math.cos(angle) y = self.y + length * math.sin(angle) if self.minute_hand: self.canvas.coords(self.minute_hand, self.x, self.y, x, y) else: self.minute_hand = self.canvas.create_line(self.x, self.y, x, y, width=2) def draw_second_hand(self, second): length = self.radius * 0.9 angle = math.pi / 30 * second - math.pi / 2 x = self.x + length * math.cos(angle) y = self.y + length * math.sin(angle) if self.second_hand: self.canvas.coords(self.second_hand, self.x, self.y, x, y) else: self.second_hand = self.canvas.create_line(self.x, self.y, x, y, fill="red", width=1) def update_clock(clock): now = time.localtime() hour = now.tm_hour minute = now.tm_min second = now.tm_sec clock.draw_hour_hand(hour + minute / 60) clock.draw_minute_hand(minute) clock.draw_second_hand(second) clock.canvas.after(1000, update_clock, clock) def main(): root = tk.Tk() root.title("Clock") canvas = tk.Canvas(root, width=400, height=400) canvas.pack() clock = Clock(canvas, 200, 200, 150) clock.draw_clock_face() clock.draw_hour_marks() update_clock(clock) root.mainloop() if __name__ == "__main__": main() ``` 在这个程序中,`Clock`类表示时钟,包括钟表的位置、半径和时针、分针、秒针的状态。`draw_clock_face()`方法用于绘制钟表的圆形,`draw_hour_marks()`方法用于绘制小时刻度线,`draw_hour_hand()`、`draw_minute_hand()`、`draw_second_hand()`方法分别用于绘制时针、分针、秒针。`update_clock()`函数用于更新时钟的状态,每隔一秒钟调用一次。在`main()`函数中,创建主窗口和画布,创建时钟对象并绘制时钟,调用`update_clock()`函数更新时钟的状态。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值