上周开始利用闲暇时间看html5 canvas技术。觉得非常好玩。就利用 canvas 做了一个简陋的钟表。源码非常简单,但是在制作的过程中,进入的误区却不少,浪费了很多时间。先上源码,然后在说说我走的岔路。

源码是基于 require.js 去写的,可以去 官网 了解 require.js 的详细资料。

 1 define(function(){
 2     function T_clock(options){
 3         this.container = options.container || document.body;
 4         if(!document.createElement('canvas').getContext){
 5             alert('当前浏览器不支持canvas!');
 6             return;
 7         }
 8         this.init();
 9     };
10     var cfg = {
11         bg:'#ffaa77',
12         bg_rbga:'rgba(255,170,119,1)',
13         outCircleLen:99,
14         innerCircleLen:94,
15         minLen:65,
16         secLen:75,
17         hourLen:55,
18         ctx:undefined
19     };
20     T_clock.prototype.init = function(){
21         var cv = document.createElement('canvas');
22         this.container.appendChild(cv);
23         cv.height = 600;
24         cv.width = 800;
25         cv.style.backgroundColor = cfg.bg;
26         if(cv.getContext){
27             cfg.ctx = cv.getContext('2d');
28         }
29         setInterval(function(){
30             cfg.ctx.clearRect(0,0,800,600);
31             cfg.ctx.beginPath();
32             cfg.ctx.lineWidth = 1;
33             cfg.ctx.arc(100,100,cfg.outCircleLen,0,2*Math.PI,false);
34             cfg.ctx.moveTo(100 + cfg.innerCircleLen,100);
35             cfg.ctx.arc(100,100,94,0,2*Math.PI,false);
36 
37             cfg.ctx.font = 'bold 14px 宋体';
38             cfg.ctx.textAlign = 'center';
39             cfg.ctx.textBaseline = 'middle';
40             var numR = 76,x,y,arc;
41             for(var i=1,arc=-Math.PI/3;i<13;i++,arc+= Math.PI/6){
42                 x = 100 + numR * Math.cos(arc);
43                 y = 100 + numR * Math.sin(arc);
44                 cfg.ctx.fillText(i,x,y);
45             }
46             for(i=0,arc=-Math.PI/2; i<60; i++,arc+=Math.PI/30){
47                 x = 100 + 94 * Math.cos(arc);
48                 y = 100 + 94 * Math.sin(arc);
49                 cfg.ctx.moveTo(x,y);
50                 if(i % 5 == 0){
51                     x = 100 + (94 - 10) * Math.cos(arc);
52                     y = 100 + (94 - 10) * Math.sin(arc);
53                 }
54                 else{
55                     x = 100 + (94 - 5) * Math.cos(arc);
56                     y = 100 + (94 - 5) * Math.sin(arc);
57                 }
58                 cfg.ctx.lineTo(x,y);
59             }
60             cfg.ctx.closePath();
61             cfg.ctx.stroke();
62 
63             var d = new Date();
64             var sec = d.getSeconds();
65             var min = d.getMinutes();
66             var hour = d.getHours();
67             var sec_angle = - Math.PI * sec / 30 + Math.PI;
68 
69             cfg.ctx.beginPath();
70             cfg.ctx.strokeStyle = '#F00';
71             cfg.ctx.lineWidth = 1;
72             cfg.ctx.moveTo(100,100);
73             cfg.ctx.lineTo(100 + cfg.secLen * Math.sin(sec_angle),100 + cfg.secLen * Math.cos(sec_angle));
74             cfg.ctx.closePath();
75             cfg.ctx.stroke();
76 
77             cfg.ctx.beginPath();
78             cfg.ctx.strokeStyle = '#555';
79             cfg.ctx.lineWidth = 2;
80             cfg.ctx.moveTo(100,100);
81             var min_angle = - Math.PI * min / 30 + Math.PI - Math.PI * sec / 1800;
82             cfg.ctx.lineTo(100 + cfg.minLen * Math.sin(min_angle),100 + cfg.minLen * Math.cos(min_angle));
83             cfg.ctx.closePath();
84             cfg.ctx.stroke();
85 
86             cfg.ctx.beginPath();
87             cfg.ctx.strokeStyle = '#000';
88             cfg.ctx.lineWidth = 2;
89             cfg.ctx.moveTo(100,100);
90             var hour_angle = - Math.PI * hour / 6 + Math.PI - Math.PI * min / 360;
91             cfg.ctx.lineTo(100 + cfg.hourLen * Math.sin(hour_angle),100 + cfg.hourLen * Math.cos(hour_angle));
92             cfg.ctx.closePath();
93             cfg.ctx.stroke();
94         },1000);
95 };
96 
97 return T_clock;
98 })
钟表模块
1     require(['../../js/require/config.js'],function(cfg){
2         require(['h5/L_canvas'],function(Canvas){
3             new Canvas({});
4         });
5     })
引用钟表模块

上效果图;

误区一:错误的理解 canvas 中的每一个图(画的每一个元素)都是一个单独的对象,可以自己销毁(擦除)自己。其实不是这样的,canvas 的 context 对象只提供了一个 clearRect 方法去清空指定区域的内容。

误区二:在一个钟表里,只有指针是动的,钟表的圆盘及数字是不动的。于是我就想在做指针动画的时候,不清除钟表的圆盘和数字,只是清除指针重画指针来实现这样的效果。期间我试了很多办法都不行。最典型的就是用了 canvas 的这个属性 globalCompositeOperation, 通过用背景色重画动画的上一个动作,用于擦除上一个动画,效果非常的莫名其妙,推测应该是 Math 对象的一些运算结果都不是精确值导致的。最后我只能在每次心跳时,清空画布重画钟表的圆盘和数字来实现动画效果;

误区三:每画一个元素,如果样式和下一个原色的样式不一样,都要单独设置元素的样式,并且要嵌在context.beginPah,context.closePath()内部,每调用closePath方法,都要调用Stroke或者fill方法把元素画出来。否则画布不会显示的。参看上传的第一块代码。