炫丽的倒计时效果Canvas绘图与动画基础

前言

想要在自己做的网页中,加入canvas动画效果,但是发现模板各种调整不好,觉得还是要对canvas有所了解,才可以让自己的网页变得狂拽炫酷吊炸天!

一、绘制基础

1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body>
 8     <canvas id="canvas" style="border:1px solid #aaa;display: block;margin: 50px auto;">
 9     </canvas>
10 
11     <script>
12         window.onload=function (ev) {
13             var canvas=document.getElementById('canvas');
14         //画布的长宽,没有单位的
15             canvas.width=1024;
16             canvas.height=600;
17             var context=canvas.getContext('2d');
18             //使用context绘制,画图之前的配置
19 
20            
21             //1.绘制折线图形
22             context.beginPath();
23             context.moveTo(100,100);
24             context.lineTo(500,500);
25             context.lineTo(100,500);
26             context.lineTo(100,100);
27             context.closePath();
28 //图线的状态,如果用context.beginPath();.... context.closePath();包住,才只对下面最近的一个 context.stroke();(画线)命令起作用,不然就对所有 context.stroke();起作用
29 
30             context.lineWidth=5;//画线的粗细
31             context.strokeStyle='#005588';
32             context.stroke();
33 
34             context.beginPath();
35             context.moveTo(200,100);
36             context.lineTo(600,500);
37             context.closePath();
38 
39             context.strokeStyle='black'; //画线的颜色
40             context.stroke();
41 
42 
43         }
44     </script>
45 </body>
46 </html>
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body>
 8     <canvas id="canvas" style="border:1px solid #aaa;display: block;margin: 50px auto;">
 9     </canvas>
10 
11     <script>
12         window.onload=function (ev) {
13             var canvas=document.getElementById('canvas');
14             canvas.width=1024;
15             canvas.height=600;
16             var context=canvas.getContext('2d');
17             //使用context绘制
18 
19             context.lineWidth=5;
20             context.strokeStyle="#005588";
21 
22             for(var i=0;i<10;i++){
23                 context.beginPath();
24                 context.arc(50+i*100,60,40,0,2*Math.PI*(i+1)/10);
25                 context.closePath();
26 
27                 context.stroke()
28             }
29 
30             for(var i=0;i<10;i++){
31                 context.beginPath();
32                 context.arc(50+i*100,180,40,0,2*Math.PI*(i+1)/10);
33                 // context.closePath();
34 
35                 context.stroke()
36             }
37 
38 
39             for(var i=0;i<10;i++){
40                 context.beginPath();
41                 context.arc(50+i*100,300,40,0,2*Math.PI*(i+1)/10,true);
42                 context.closePath();
43 
44                 context.stroke()
45             }
46 
47             for(var i=0;i<10;i++){
48                 context.beginPath();
49                 context.arc(50+i*100,420,40,0,2*Math.PI*(i+1)/10,true);
50                 // context.closePath();
51 
52                 context.stroke()
53             }
54 
55             context.fillStyle="#005588";
56             for(var i=0;i<10;i++){
57                 context.beginPath();
58                 context.arc(50+i*100,540,40,0,2*Math.PI*(i+1)/10);
59                 context.closePath();
60 
61                 context.fill()
62             }
63             
64         }
65     </script>
66 </body>
67 </html>

二、倒计时电子钟的实现

新建两个js文件:digit.js 存放一个三维数组,countdown.js实现时钟效果

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body>
 8     <canvas id="canvas" style="border:1px solid #aaa;display: block;margin: 50px auto;">
 9     </canvas>
10 
11     <script src="digit.js"></script>
12     <script src="countdown.js"></script>
13 
14 </body>
15 </html>
  1 digit =
  2     [
  3         [
  4             [0,0,1,1,1,0,0],
  5             [0,1,1,0,1,1,0],
  6             [1,1,0,0,0,1,1],
  7             [1,1,0,0,0,1,1],
  8             [1,1,0,0,0,1,1],
  9             [1,1,0,0,0,1,1],
 10             [1,1,0,0,0,1,1],
 11             [1,1,0,0,0,1,1],
 12             [0,1,1,0,1,1,0],
 13             [0,0,1,1,1,0,0]
 14         ],//0
 15         [
 16             [0,0,0,1,1,0,0],
 17             [0,1,1,1,1,0,0],
 18             [0,0,0,1,1,0,0],
 19             [0,0,0,1,1,0,0],
 20             [0,0,0,1,1,0,0],
 21             [0,0,0,1,1,0,0],
 22             [0,0,0,1,1,0,0],
 23             [0,0,0,1,1,0,0],
 24             [0,0,0,1,1,0,0],
 25             [1,1,1,1,1,1,1]
 26         ],//1
 27         [
 28             [0,1,1,1,1,1,0],
 29             [1,1,0,0,0,1,1],
 30             [0,0,0,0,0,1,1],
 31             [0,0,0,0,1,1,0],
 32             [0,0,0,1,1,0,0],
 33             [0,0,1,1,0,0,0],
 34             [0,1,1,0,0,0,0],
 35             [1,1,0,0,0,0,0],
 36             [1,1,0,0,0,1,1],
 37             [1,1,1,1,1,1,1]
 38         ],//2
 39         [
 40             [1,1,1,1,1,1,1],
 41             [0,0,0,0,0,1,1],
 42             [0,0,0,0,1,1,0],
 43             [0,0,0,1,1,0,0],
 44             [0,0,1,1,1,0,0],
 45             [0,0,0,0,1,1,0],
 46             [0,0,0,0,0,1,1],
 47             [0,0,0,0,0,1,1],
 48             [1,1,0,0,0,1,1],
 49             [0,1,1,1,1,1,0]
 50         ],//3
 51         [
 52             [0,0,0,0,1,1,0],
 53             [0,0,0,1,1,1,0],
 54             [0,0,1,1,1,1,0],
 55             [0,1,1,0,1,1,0],
 56             [1,1,0,0,1,1,0],
 57             [1,1,1,1,1,1,1],
 58             [0,0,0,0,1,1,0],
 59             [0,0,0,0,1,1,0],
 60             [0,0,0,0,1,1,0],
 61             [0,0,0,1,1,1,1]
 62         ],//4
 63         [
 64             [1,1,1,1,1,1,1],
 65             [1,1,0,0,0,0,0],
 66             [1,1,0,0,0,0,0],
 67             [1,1,1,1,1,1,0],
 68             [0,0,0,0,0,1,1],
 69             [0,0,0,0,0,1,1],
 70             [0,0,0,0,0,1,1],
 71             [0,0,0,0,0,1,1],
 72             [1,1,0,0,0,1,1],
 73             [0,1,1,1,1,1,0]
 74         ],//5
 75         [
 76             [0,0,0,0,1,1,0],
 77             [0,0,1,1,0,0,0],
 78             [0,1,1,0,0,0,0],
 79             [1,1,0,0,0,0,0],
 80             [1,1,0,1,1,1,0],
 81             [1,1,0,0,0,1,1],
 82             [1,1,0,0,0,1,1],
 83             [1,1,0,0,0,1,1],
 84             [1,1,0,0,0,1,1],
 85             [0,1,1,1,1,1,0]
 86         ],//6
 87         [
 88             [1,1,1,1,1,1,1],
 89             [1,1,0,0,0,1,1],
 90             [0,0,0,0,1,1,0],
 91             [0,0,0,0,1,1,0],
 92             [0,0,0,1,1,0,0],
 93             [0,0,0,1,1,0,0],
 94             [0,0,1,1,0,0,0],
 95             [0,0,1,1,0,0,0],
 96             [0,0,1,1,0,0,0],
 97             [0,0,1,1,0,0,0]
 98         ],//7
 99         [
100             [0,1,1,1,1,1,0],
101             [1,1,0,0,0,1,1],
102             [1,1,0,0,0,1,1],
103             [1,1,0,0,0,1,1],
104             [0,1,1,1,1,1,0],
105             [1,1,0,0,0,1,1],
106             [1,1,0,0,0,1,1],
107             [1,1,0,0,0,1,1],
108             [1,1,0,0,0,1,1],
109             [0,1,1,1,1,1,0]
110         ],//8
111         [
112             [0,1,1,1,1,1,0],
113             [1,1,0,0,0,1,1],
114             [1,1,0,0,0,1,1],
115             [1,1,0,0,0,1,1],
116             [0,1,1,1,0,1,1],
117             [0,0,0,0,0,1,1],
118             [0,0,0,0,0,1,1],
119             [0,0,0,0,1,1,0],
120             [0,0,0,1,1,0,0],
121             [0,1,1,0,0,0,0]
122         ],//9
123         [
124             [0,0,0,0],
125             [0,0,0,0],
126             [0,1,1,0],
127             [0,1,1,0],
128             [0,0,0,0],
129             [0,0,0,0],
130             [0,1,1,0],
131             [0,1,1,0],
132             [0,0,0,0],
133             [0,0,0,0]
134         ]//:
135     ];
 1 var WINDOW_WIDTH=1024;
 2 var WINDOW_HEIGHT=600;
 3 var RADIUS=8;
 4 var MARGIN_TOP=60;
 5 var MARGIN_LIFT=30;
 6 const endTime=new Date("2018/3/20,18:47:52");//js中的月份是从0-11,如果要表示7月,则用6表示,const代表常量
 7 var curShowTimeSeconds=0;
 8 
 9 
10 window.onload=function () {
11 
12     var canvas=document.getElementById('canvas');
13     var context=canvas.getContext('2d');
14 
15     canvas.width=WINDOW_WIDTH;
16     canvas.height=WINDOW_HEIGHT;
17 
18     curShowTimeSeconds=getCurrentShowTimeSeconds();
19     render(context);
20 };
21 
22 function getCurrentShowTimeSeconds() {
23     var curTime=new Date();
24     var ret=endTime.getTime()-curTime.getTime();
25     ret=Math.round(ret/1000);
26     return ret>=0?ret:0;
27 }
28 
29 
30 function render(cxt) {
31     var hours=parseInt(curShowTimeSeconds/3600);
32     var minutes=parseInt((curShowTimeSeconds-hours * 3600)/60);
33     var seconds=curShowTimeSeconds%60;
34 
35     renderDigit(MARGIN_LIFT,MARGIN_TOP,parseInt(hours/10),cxt);
36     renderDigit(MARGIN_LIFT+15*(RADIUS+1),MARGIN_TOP,parseInt(hours%10),cxt);
37     renderDigit(MARGIN_LIFT+30*(RADIUS+1),MARGIN_TOP,10,cxt);
38     renderDigit(MARGIN_LIFT+39*(RADIUS+1),MARGIN_TOP,parseInt(minutes/10),cxt);
39     renderDigit(MARGIN_LIFT+54*(RADIUS+1),MARGIN_TOP,parseInt(minutes%10),cxt);
40     renderDigit(MARGIN_LIFT+69*(RADIUS+1),MARGIN_TOP,parseInt(10),cxt);
41     renderDigit(MARGIN_LIFT+78*(RADIUS+1),MARGIN_TOP,parseInt(seconds/10),cxt);
42     renderDigit(MARGIN_LIFT+93*(RADIUS+1),MARGIN_TOP,parseInt(seconds%10),cxt);
43 
44 }
45 
46 function renderDigit(x,y,num,cxt) {
47     cxt.fillStyle="rgb(0,102,153)";
48 
49     for(var i=0;i<digit[num].length;i++)
50         for(var j=0;j<digit[num][i].length;j++)
51             if(digit[num][i][j]==1){
52             cxt.beginPath();
53             cxt.arc(x+j*2*(RADIUS+1)+(RADIUS+1),y+i*2*(RADIUS+1)+(RADIUS+1),RADIUS,0,2*Math.PI);
54             cxt.closePath();
55 
56             cxt.fill()
57             }
58 }

注意:数组阵列中,只能从0-9,如果超过10,就会报错,换言之,定义的到期时间距离程序当前时间不能超过100个小时,也就是四天。

三、倒计时电子钟的实现

1.实现倒计时效果,修改countdown.js

 1 var WINDOW_WIDTH=1024;
 2 var WINDOW_HEIGHT=600;
 3 var RADIUS=8;
 4 var MARGIN_TOP=60;
 5 var MARGIN_LIFT=30;
 6 const endTime=new Date("2018/3/20,18:47:52");//js中的月份是从0-11,如果要表示7月,则用6表示,const代表常量
 7 var curShowTimeSeconds=0;
 8 
 9 
10 window.onload=function () {
11 
12     var canvas=document.getElementById('canvas');
13     var context=canvas.getContext('2d');
14 
15     canvas.width=WINDOW_WIDTH;
16     canvas.height=WINDOW_HEIGHT;
17 
18     curShowTimeSeconds=getCurrentShowTimeSeconds();
19     // render(context);
20     setInterval(function () {
21         render(context);
22         updata();
23 
24     },
25         50)
26 };
27 
28 function updata() {
29     var nextShowTimeSeconds=getCurrentShowTimeSeconds();
30 
31     var nextHours=parseInt(nextShowTimeSeconds/3600);
32     var nextMinutes=parseInt((nextShowTimeSeconds-nextHours*3600)/60);
33     var nextSeconds=nextShowTimeSeconds%60;
34 
35     var curHours=parseInt(curShowTimeSeconds/3600);
36     var curMinutes=parseInt((curShowTimeSeconds-curHours * 3600)/60);
37     var curSeconds=curShowTimeSeconds%60;
38 
39     if(nextSeconds!=curSeconds){
40         curShowTimeSeconds=nextShowTimeSeconds;
41     }
42 
43 
44 }
45 
46 function getCurrentShowTimeSeconds() {
47     var curTime=new Date();
48     var ret=endTime.getTime()-curTime.getTime();
49     ret=Math.round(ret/1000);
50     return ret>=0?ret:0;
51 }
52 
53 function render(cxt) {
54 
55     cxt.clearRect(0,0,WINDOW_WIDTH,WINDOW_HEIGHT);//对整个画布进行刷新,防止新画的跟以前画的叠加
56     var hours=parseInt(curShowTimeSeconds/3600);
57     var minutes=parseInt((curShowTimeSeconds-hours * 3600)/60);
58     var seconds=curShowTimeSeconds%60;
59 
60     renderDigit(MARGIN_LIFT,MARGIN_TOP,parseInt(hours/10),cxt);
61     renderDigit(MARGIN_LIFT+15*(RADIUS+1),MARGIN_TOP,parseInt(hours%10),cxt);
62     renderDigit(MARGIN_LIFT+30*(RADIUS+1),MARGIN_TOP,10,cxt);
63     renderDigit(MARGIN_LIFT+39*(RADIUS+1),MARGIN_TOP,parseInt(minutes/10),cxt);
64     renderDigit(MARGIN_LIFT+54*(RADIUS+1),MARGIN_TOP,parseInt(minutes%10),cxt);
65     renderDigit(MARGIN_LIFT+69*(RADIUS+1),MARGIN_TOP,parseInt(10),cxt);
66     renderDigit(MARGIN_LIFT+78*(RADIUS+1),MARGIN_TOP,parseInt(seconds/10),cxt);
67     renderDigit(MARGIN_LIFT+93*(RADIUS+1),MARGIN_TOP,parseInt(seconds%10),cxt);
68 
69 }
70 
71 function renderDigit(x,y,num,cxt) {
72     cxt.fillStyle="rgb(0,102,153)";
73 
74     for(var i=0;i<digit[num].length;i++)
75         for(var j=0;j<digit[num][i].length;j++)
76             if(digit[num][i][j]==1){
77             cxt.beginPath();
78             cxt.arc(x+j*2*(RADIUS+1)+(RADIUS+1),y+i*2*(RADIUS+1)+(RADIUS+1),RADIUS,0,2*Math.PI);
79             cxt.closePath();
80 
81             cxt.fill()
82             }
83 }

各一秒变化一次:

2.使用canvas做一个物理小球

新建一个physical.html

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body>
 8     <canvas id="canvas" style="border:1px solid #aaa;display: block;margin: 50px auto;"></canvas>
 9 </body>
10 
11 <script>
12     var ball={x:512,y:100,r:20,g:2,vx:-4,vy:0,color:"#005588"};
13     window.onload=function (ev) {
14         var canvas=document.getElementById("canvas");
15         canvas.width=1024;
16         canvas.height=600;
17         var context=canvas.getContext("2d");
18         //每隔50毫秒,生成一次小球,并更新下一次小球出现的位置和速度
19         setInterval(function () {
20             render(context);
21             update();
22         },50)
23     };
24     function render(cxt) {
25         cxt.clearRect(0,0,cxt.canvas.width,cxt.canvas.height);
26 
27         cxt.fillStyle=ball.color;
28         cxt.beginPath();
29         cxt.arc(ball.x,ball.y,ball.r,0,2*Math.PI)
30         cxt.closePath();
31 
32         cxt.fill();
33     }
34     function update() {
35         ball.x+=ball.vx;
36         ball.y+=ball.vy;
37         ball.vy+=ball.g;
38         if(ball.y>canvas.height-ball.r){
39             ball.y=canvas.height-ball.r;
40             ball.vy=-ball.vy*0.5;
41         }
42     }
43 
44 </script>
45 </html>

3.根据物理小球的例子,对countdown.js进行改写:

1.声明一个数组存放所有产生的小球,声明另一个数组存放几种颜色,用于随机取出赋给小球,以达到多种颜色小球的效果:

 1 //小球
 2 var balls = [];
 3 const colors = ["#33B5E5",
 4                 "#0099CC",
 5                 "#AA66CC",
 6                 "#9933CC",
 7                 "#99CC00",
 8                 "#669900",
 9                 "#FFBB33",
10                 "#FF8800",
11                 "#FF4444",
12                 "#CC0000"]

2.在update()函数中增加判断生成物理小球的条件,以及调用更新小球所在位置和速度的函数updateBalls():

 1 if(nextSeconds!=curSeconds){
 2         //小球
 3         if( parseInt(curHours/10) != parseInt(nextHours/10) ){
 4             addBalls( MARGIN_LIFT + 0 , MARGIN_TOP , parseInt(curHours/10) );
 5         }
 6         if( parseInt(curHours%10) != parseInt(nextHours%10) ){
 7             addBalls( MARGIN_LIFT + 15*(RADIUS+1) , MARGIN_TOP , parseInt(curHours/10) );
 8         }
 9 
10         if( parseInt(curMinutes/10) != parseInt(nextMinutes/10) ){
11             addBalls( MARGIN_LIFT + 39*(RADIUS+1) , MARGIN_TOP , parseInt(curMinutes/10) );
12         }
13         if( parseInt(curMinutes%10) != parseInt(nextMinutes%10) ){
14             addBalls( MARGIN_LIFT + 54*(RADIUS+1) , MARGIN_TOP , parseInt(curMinutes%10) );
15         }
16 
17         if( parseInt(curSeconds/10) != parseInt(nextSeconds/10) ){
18             addBalls( MARGIN_LIFT + 78*(RADIUS+1) , MARGIN_TOP , parseInt(curSeconds/10) );
19         }
20         if( parseInt(curSeconds%10) != parseInt(nextSeconds%10) ){
21             addBalls( MARGIN_LIFT + 93*(RADIUS+1) , MARGIN_TOP , parseInt(nextSeconds%10) );
22         }
23 
24         //更新时间
25         curShowTimeSeconds=nextShowTimeSeconds;
26     }
27     //更新小球的速度
28     updateBalls();

3.定义设置要生成的小球的各种参数的函数addBalls():

 1 function addBalls( x , y , num ){
 2     console.log("addBalls");
 3     for( var i=0;i<digit[num].length;i++)
 4         for(var j=0;j<digit[num][i].length;j++)
 5             if( digit[num][i][j] == 1 ){
 6                 var aBall = {
 7                     x:x+j*2*(RADIUS+1)+(RADIUS+1),
 8                     y:y+i*2*(RADIUS+1)+(RADIUS+1),
 9                     g:1.5+Math.random(),
10                     vx:Math.pow( -1 , Math.ceil( Math.random()*1000 ) ) * 4,
11                     vy:-5,
12                     color: colors[ Math.floor( Math.random()*colors.length ) ]
13                 };
14                 balls.push(aBall);
15             }
16 }

4.定义更新小球位置和速度的函数updateBalls():

 1 function updateBalls(){
 2     for( var i = 0 ; i < balls.length ; i ++ ){
 3         balls[i].x += balls[i].vx;
 4         balls[i].y += balls[i].vy;
 5         balls[i].vy += balls[i].g;
 6         if( balls[i].y >= WINDOW_HEIGHT-RADIUS ){
 7             balls[i].y = WINDOW_HEIGHT-RADIUS;
 8             balls[i].vy = - balls[i].vy*0.75;
 9         }
10     }
11 }

5.在render函数中增加画物理小球的代码:

1 for(var i=0;i<balls.length;i++){
2         cxt.fillStyle=balls[i].color;
3 
4         cxt.beginPath();
5         cxt.arc(balls[i].x,balls[i].y,RADIUS,0,2*Math.PI,true);
6         cxt.closePath();
7 
8         cxt.fill();
9     }

修改后的countdown.js为:

  1 var WINDOW_WIDTH=1024;
  2 var WINDOW_HEIGHT=600;
  3 var RADIUS=8;
  4 var MARGIN_TOP=60;
  5 var MARGIN_LIFT=30;
  6 const endTime=new Date("2018/3/20,18:47:52");//const代表常量
  7 var curShowTimeSeconds=0;
  8 
  9 //小球
 10 var balls = [];
 11 const colors = ["#33B5E5",
 12                 "#0099CC",
 13                 "#AA66CC",
 14                 "#9933CC",
 15                 "#99CC00",
 16                 "#669900",
 17                 "#FFBB33",
 18                 "#FF8800",
 19                 "#FF4444",
 20                 "#CC0000"]
 21 
 22 window.onload=function () {
 23 
 24     var canvas=document.getElementById('canvas');
 25     var context=canvas.getContext('2d');
 26 
 27     canvas.width=WINDOW_WIDTH;
 28     canvas.height=WINDOW_HEIGHT;
 29 
 30     curShowTimeSeconds=getCurrentShowTimeSeconds();
 31     // render(context);
 32     setInterval(function () {
 33         render(context);
 34         update();
 35     },
 36         50)
 37 };
 38 
 39 function update() {
 40     var nextShowTimeSeconds=getCurrentShowTimeSeconds();
 41 
 42     var nextHours=parseInt(nextShowTimeSeconds/3600);
 43     var nextMinutes=parseInt((nextShowTimeSeconds-nextHours*3600)/60);
 44     var nextSeconds=nextShowTimeSeconds%60;
 45 
 46     var curHours=parseInt(curShowTimeSeconds/3600);
 47     var curMinutes=parseInt((curShowTimeSeconds-curHours * 3600)/60);
 48     var curSeconds=curShowTimeSeconds%60;
 49 
 50     if(nextSeconds!=curSeconds){
 51         //小球
 52         if( parseInt(curHours/10) != parseInt(nextHours/10) ){
 53             addBalls( MARGIN_LIFT + 0 , MARGIN_TOP , parseInt(curHours/10) );
 54         }
 55         if( parseInt(curHours%10) != parseInt(nextHours%10) ){
 56             addBalls( MARGIN_LIFT + 15*(RADIUS+1) , MARGIN_TOP , parseInt(curHours/10) );
 57         }
 58 
 59         if( parseInt(curMinutes/10) != parseInt(nextMinutes/10) ){
 60             addBalls( MARGIN_LIFT + 39*(RADIUS+1) , MARGIN_TOP , parseInt(curMinutes/10) );
 61         }
 62         if( parseInt(curMinutes%10) != parseInt(nextMinutes%10) ){
 63             addBalls( MARGIN_LIFT + 54*(RADIUS+1) , MARGIN_TOP , parseInt(curMinutes%10) );
 64         }
 65 
 66         if( parseInt(curSeconds/10) != parseInt(nextSeconds/10) ){
 67             addBalls( MARGIN_LIFT + 78*(RADIUS+1) , MARGIN_TOP , parseInt(curSeconds/10) );
 68         }
 69         if( parseInt(curSeconds%10) != parseInt(nextSeconds%10) ){
 70             addBalls( MARGIN_LIFT + 93*(RADIUS+1) , MARGIN_TOP , parseInt(nextSeconds%10) );
 71         }
 72 
 73         //更新时间
 74         curShowTimeSeconds=nextShowTimeSeconds;
 75     }
 76     //更新小球的速度
 77     updateBalls();
 78 
 79 }
 80 
 81 function updateBalls(){
 82     for( var i = 0 ; i < balls.length ; i ++ ){
 83         balls[i].x += balls[i].vx;
 84         balls[i].y += balls[i].vy;
 85         balls[i].vy += balls[i].g;
 86         if( balls[i].y >= WINDOW_HEIGHT-RADIUS ){
 87             balls[i].y = WINDOW_HEIGHT-RADIUS;
 88             balls[i].vy = - balls[i].vy*0.75;
 89         }
 90     }
 91 }
 92 
 93 function addBalls( x , y , num ){
 94     console.log("addBalls");
 95     for( var i=0;i<digit[num].length;i++)
 96         for(var j=0;j<digit[num][i].length;j++)
 97             if( digit[num][i][j] == 1 ){
 98                 var aBall = {
 99                     x:x+j*2*(RADIUS+1)+(RADIUS+1),
100                     y:y+i*2*(RADIUS+1)+(RADIUS+1),
101                     g:1.5+Math.random(),
102                     vx:Math.pow( -1 , Math.ceil( Math.random()*1000 ) ) * 4,
103                     vy:-5,
104                     color: colors[ Math.floor( Math.random()*colors.length ) ]
105                 };
106                 balls.push(aBall);
107             }
108 }
109 
110 function getCurrentShowTimeSeconds() {
111     var curTime=new Date();
112     var ret=endTime.getTime()-curTime.getTime();
113     ret=Math.round(ret/1000);
114     return ret>=0?ret:0;
115 }
116 
117 function render(cxt) {
118 
119     cxt.clearRect(0,0,WINDOW_WIDTH,WINDOW_HEIGHT);//对整个画布进行刷新,防止新画的跟以前画的叠加
120     var hours=parseInt(curShowTimeSeconds/3600);
121     var minutes=parseInt((curShowTimeSeconds-hours * 3600)/60);
122     var seconds=curShowTimeSeconds%60;
123 
124     renderDigit(MARGIN_LIFT,MARGIN_TOP,parseInt(hours/10),cxt);
125     renderDigit(MARGIN_LIFT+15*(RADIUS+1),MARGIN_TOP,parseInt(hours%10),cxt);
126     renderDigit(MARGIN_LIFT+30*(RADIUS+1),MARGIN_TOP,10,cxt);
127     renderDigit(MARGIN_LIFT+39*(RADIUS+1),MARGIN_TOP,parseInt(minutes/10),cxt);
128     renderDigit(MARGIN_LIFT+54*(RADIUS+1),MARGIN_TOP,parseInt(minutes%10),cxt);
129     renderDigit(MARGIN_LIFT+69*(RADIUS+1),MARGIN_TOP,parseInt(10),cxt);
130     renderDigit(MARGIN_LIFT+78*(RADIUS+1),MARGIN_TOP,parseInt(seconds/10),cxt);
131     renderDigit(MARGIN_LIFT+93*(RADIUS+1),MARGIN_TOP,parseInt(seconds%10),cxt);
132 
133     for(var i=0;i<balls.length;i++){
134         cxt.fillStyle=balls[i].color;
135 
136         cxt.beginPath();
137         cxt.arc(balls[i].x,balls[i].y,RADIUS,0,2*Math.PI,true);
138         cxt.closePath();
139 
140         cxt.fill();
141     }
142 
143 }
144 
145 function renderDigit(x,y,num,cxt) {
146     cxt.fillStyle="rgb(0,102,153)";
147 
148     for(var i=0;i<digit[num].length;i++)
149         for(var j=0;j<digit[num][i].length;j++)
150             if(digit[num][i][j]==1){
151             cxt.beginPath();
152             cxt.arc(x+j*2*(RADIUS+1)+(RADIUS+1),y+i*2*(RADIUS+1)+(RADIUS+1),RADIUS,0,2*Math.PI);
153             cxt.closePath();
154 
155             cxt.fill()
156             }
157 }

效果达到了,不过小球会越来越多,最后弄得网页很卡很卡,对于代码的性能优化,是具有必要性的!

四、性能优化、扩展

1.优化:

改写countdown.js给updateBalls增加代码,让物理小球离开画布的,全都消失解放内存。(原视频教程中bug,我用另外的方法实现的,效果不错)

1     var cnt=[];
2     for(var i=0;i<balls.length;i++){
3         if(balls[i].x-RADIUS>0&&balls[i].x+RADIUS<WINDOW_WIDTH){
4             cnt.push(balls[i]);
5         }
6     }
7     balls=cnt;

修改以后的countdown.js代码:

  1 var WINDOW_WIDTH=1024;
  2 var WINDOW_HEIGHT=600;
  3 var RADIUS=8;
  4 var MARGIN_TOP=60;
  5 var MARGIN_LIFT=30;
  6 const endTime=new Date("2018/3/20,18:47:52");//const代表常量
  7 var curShowTimeSeconds=0;
  8 
  9 //小球
 10 var balls = [];
 11 const colors = ["#33B5E5",
 12                 "#0099CC",
 13                 "#AA66CC",
 14                 "#9933CC",
 15                 "#99CC00",
 16                 "#669900",
 17                 "#FFBB33",
 18                 "#FF8800",
 19                 "#FF4444",
 20                 "#CC0000"]
 21 
 22 window.onload=function () {
 23 
 24     var canvas=document.getElementById('canvas');
 25     var context=canvas.getContext('2d');
 26 
 27     canvas.width=WINDOW_WIDTH;
 28     canvas.height=WINDOW_HEIGHT;
 29 
 30     curShowTimeSeconds=getCurrentShowTimeSeconds();
 31     // render(context);
 32     setInterval(function () {
 33         render(context);
 34         update();
 35         //打印物理小球个数
 36         console.log(balls.length);
 37     },
 38         50)
 39 };
 40 
 41 function update() {
 42     var nextShowTimeSeconds=getCurrentShowTimeSeconds();
 43 
 44     var nextHours=parseInt(nextShowTimeSeconds/3600);
 45     var nextMinutes=parseInt((nextShowTimeSeconds-nextHours*3600)/60);
 46     var nextSeconds=nextShowTimeSeconds%60;
 47 
 48     var curHours=parseInt(curShowTimeSeconds/3600);
 49     var curMinutes=parseInt((curShowTimeSeconds-curHours * 3600)/60);
 50     var curSeconds=curShowTimeSeconds%60;
 51 
 52     if(nextSeconds!=curSeconds){
 53         //小球
 54         if( parseInt(curHours/10) != parseInt(nextHours/10) ){
 55             addBalls( MARGIN_LIFT + 0 , MARGIN_TOP , parseInt(curHours/10) );
 56         }
 57         if( parseInt(curHours%10) != parseInt(nextHours%10) ){
 58             addBalls( MARGIN_LIFT + 15*(RADIUS+1) , MARGIN_TOP , parseInt(curHours/10) );
 59         }
 60 
 61         if( parseInt(curMinutes/10) != parseInt(nextMinutes/10) ){
 62             addBalls( MARGIN_LIFT + 39*(RADIUS+1) , MARGIN_TOP , parseInt(curMinutes/10) );
 63         }
 64         if( parseInt(curMinutes%10) != parseInt(nextMinutes%10) ){
 65             addBalls( MARGIN_LIFT + 54*(RADIUS+1) , MARGIN_TOP , parseInt(curMinutes%10) );
 66         }
 67 
 68         if( parseInt(curSeconds/10) != parseInt(nextSeconds/10) ){
 69             addBalls( MARGIN_LIFT + 78*(RADIUS+1) , MARGIN_TOP , parseInt(curSeconds/10) );
 70         }
 71         if( parseInt(curSeconds%10) != parseInt(nextSeconds%10) ){
 72             addBalls( MARGIN_LIFT + 93*(RADIUS+1) , MARGIN_TOP , parseInt(nextSeconds%10) );
 73         }
 74 
 75         //更新时间
 76         curShowTimeSeconds=nextShowTimeSeconds;
 77     }
 78     //更新小球的速度
 79     updateBalls();
 80 
 81 }
 82 
 83 function updateBalls(){
 84     for( var i = 0 ; i < balls.length ; i ++ ){
 85         balls[i].x += balls[i].vx;
 86         balls[i].y += balls[i].vy;
 87         balls[i].vy += balls[i].g;
 88         if( balls[i].y >= WINDOW_HEIGHT-RADIUS ){
 89             balls[i].y = WINDOW_HEIGHT-RADIUS;
 90             balls[i].vy = - balls[i].vy*0.75;
 91         }
 92     }
 93 
 94     var cnt=[];
 95     for(var i=0;i<balls.length;i++){
 96         if(balls[i].x-RADIUS>0&&balls[i].x+RADIUS<WINDOW_WIDTH){
 97             cnt.push(balls[i]);
 98         }
 99     }
100     balls=cnt;
101 
102 }
103 
104 function addBalls( x , y , num ){
105     for( var i=0;i<digit[num].length;i++)
106         for(var j=0;j<digit[num][i].length;j++)
107             if( digit[num][i][j] == 1 ){
108                 var aBall = {
109                     x:x+j*2*(RADIUS+1)+(RADIUS+1),
110                     y:y+i*2*(RADIUS+1)+(RADIUS+1),
111                     g:1.5+Math.random(),
112                     vx:Math.pow( -1 , Math.ceil( Math.random()*1000 ) ) * 4,
113                     vy:-5,
114                     color: colors[ Math.floor( Math.random()*colors.length ) ]
115                 };
116                 balls.push(aBall);
117             }
118 }
119 
120 function getCurrentShowTimeSeconds() {
121     var curTime=new Date();
122     var ret=endTime.getTime()-curTime.getTime();
123     ret=Math.round(ret/1000);
124     return ret>=0?ret:0;
125 }
126 
127 function render(cxt) {
128 
129     cxt.clearRect(0,0,WINDOW_WIDTH,WINDOW_HEIGHT);//对整个画布进行刷新,防止新画的跟以前画的叠加
130     var hours=parseInt(curShowTimeSeconds/3600);
131     var minutes=parseInt((curShowTimeSeconds-hours * 3600)/60);
132     var seconds=curShowTimeSeconds%60;
133 
134     renderDigit(MARGIN_LIFT,MARGIN_TOP,parseInt(hours/10),cxt);
135     renderDigit(MARGIN_LIFT+15*(RADIUS+1),MARGIN_TOP,parseInt(hours%10),cxt);
136     renderDigit(MARGIN_LIFT+30*(RADIUS+1),MARGIN_TOP,10,cxt);
137     renderDigit(MARGIN_LIFT+39*(RADIUS+1),MARGIN_TOP,parseInt(minutes/10),cxt);
138     renderDigit(MARGIN_LIFT+54*(RADIUS+1),MARGIN_TOP,parseInt(minutes%10),cxt);
139     renderDigit(MARGIN_LIFT+69*(RADIUS+1),MARGIN_TOP,parseInt(10),cxt);
140     renderDigit(MARGIN_LIFT+78*(RADIUS+1),MARGIN_TOP,parseInt(seconds/10),cxt);
141     renderDigit(MARGIN_LIFT+93*(RADIUS+1),MARGIN_TOP,parseInt(seconds%10),cxt);
142 
143     for(var i=0;i<balls.length;i++){
144         cxt.fillStyle=balls[i].color;
145 
146         cxt.beginPath();
147         cxt.arc(balls[i].x,balls[i].y,RADIUS,0,2*Math.PI,true);
148         cxt.closePath();
149 
150         cxt.fill();
151     }
152 
153 }
154 
155 function renderDigit(x,y,num,cxt) {
156     cxt.fillStyle="rgb(0,102,153)";
157 
158     for(var i=0;i<digit[num].length;i++)
159         for(var j=0;j<digit[num][i].length;j++)
160             if(digit[num][i][j]==1){
161             cxt.beginPath();
162             cxt.arc(x+j*2*(RADIUS+1)+(RADIUS+1),y+i*2*(RADIUS+1)+(RADIUS+1),RADIUS,0,2*Math.PI);
163             cxt.closePath();
164 
165             cxt.fill()
166             }
167 }

效果图:

2.扩展改写为时钟

改写countdown.js中的getCurrentShowTimeSeconds函数:

1 function getCurrentShowTimeSeconds() {
2     var curTime=new Date();
3     var ret=curTime.getHours()*3600+curTime.getMinutes()*60+curTime.getSeconds();
4     return ret;
5 }

countdown.js时钟代码:

  1 var WINDOW_WIDTH=1024;
  2 var WINDOW_HEIGHT=600;
  3 var RADIUS=8;
  4 var MARGIN_TOP=60;
  5 var MARGIN_LIFT=30;
  6 //const endTime=new Date("2018/3/20,18:47:52");//const代表常量
  7 var curShowTimeSeconds=0;
  8 
  9 //小球
 10 var balls = [];
 11 const colors = ["#33B5E5",
 12                 "#0099CC",
 13                 "#AA66CC",
 14                 "#9933CC",
 15                 "#99CC00",
 16                 "#669900",
 17                 "#FFBB33",
 18                 "#FF8800",
 19                 "#FF4444",
 20                 "#CC0000"]
 21 
 22 window.onload=function () {
 23 
 24     var canvas=document.getElementById('canvas');
 25     var context=canvas.getContext('2d');
 26 
 27     canvas.width=WINDOW_WIDTH;
 28     canvas.height=WINDOW_HEIGHT;
 29 
 30     curShowTimeSeconds=getCurrentShowTimeSeconds();
 31     // render(context);
 32     setInterval(function () {
 33         render(context);
 34         update();
 35         //打印物理小球个数
 36         console.log(balls.length);
 37     },
 38         50)
 39 };
 40 
 41 function update() {
 42     var nextShowTimeSeconds=getCurrentShowTimeSeconds();
 43 
 44     var nextHours=parseInt(nextShowTimeSeconds/3600);
 45     var nextMinutes=parseInt((nextShowTimeSeconds-nextHours*3600)/60);
 46     var nextSeconds=nextShowTimeSeconds%60;
 47 
 48     var curHours=parseInt(curShowTimeSeconds/3600);
 49     var curMinutes=parseInt((curShowTimeSeconds-curHours * 3600)/60);
 50     var curSeconds=curShowTimeSeconds%60;
 51 
 52     if(nextSeconds!=curSeconds){
 53         //小球
 54         if( parseInt(curHours/10) != parseInt(nextHours/10) ){
 55             addBalls( MARGIN_LIFT + 0 , MARGIN_TOP , parseInt(curHours/10) );
 56         }
 57         if( parseInt(curHours%10) != parseInt(nextHours%10) ){
 58             addBalls( MARGIN_LIFT + 15*(RADIUS+1) , MARGIN_TOP , parseInt(curHours/10) );
 59         }
 60 
 61         if( parseInt(curMinutes/10) != parseInt(nextMinutes/10) ){
 62             addBalls( MARGIN_LIFT + 39*(RADIUS+1) , MARGIN_TOP , parseInt(curMinutes/10) );
 63         }
 64         if( parseInt(curMinutes%10) != parseInt(nextMinutes%10) ){
 65             addBalls( MARGIN_LIFT + 54*(RADIUS+1) , MARGIN_TOP , parseInt(curMinutes%10) );
 66         }
 67 
 68         if( parseInt(curSeconds/10) != parseInt(nextSeconds/10) ){
 69             addBalls( MARGIN_LIFT + 78*(RADIUS+1) , MARGIN_TOP , parseInt(curSeconds/10) );
 70         }
 71         if( parseInt(curSeconds%10) != parseInt(nextSeconds%10) ){
 72             addBalls( MARGIN_LIFT + 93*(RADIUS+1) , MARGIN_TOP , parseInt(nextSeconds%10) );
 73         }
 74 
 75         //更新时间
 76         curShowTimeSeconds=nextShowTimeSeconds;
 77     }
 78     //更新小球的速度
 79     updateBalls();
 80 
 81 }
 82 
 83 function updateBalls(){
 84     for( var i = 0 ; i < balls.length ; i ++ ){
 85         balls[i].x += balls[i].vx;
 86         balls[i].y += balls[i].vy;
 87         balls[i].vy += balls[i].g;
 88         if( balls[i].y >= WINDOW_HEIGHT-RADIUS ){
 89             balls[i].y = WINDOW_HEIGHT-RADIUS;
 90             balls[i].vy = - balls[i].vy*0.75;
 91         }
 92     }
 93 
 94     var cnt=[];
 95     for(var i=0;i<balls.length;i++){
 96         if(balls[i].x-RADIUS>0&&balls[i].x+RADIUS<WINDOW_WIDTH){
 97             cnt.push(balls[i]);
 98         }
 99     }
100     balls=cnt;
101 
102 }
103 
104 function addBalls( x , y , num ){
105     for( var i=0;i<digit[num].length;i++)
106         for(var j=0;j<digit[num][i].length;j++)
107             if( digit[num][i][j] == 1 ){
108                 var aBall = {
109                     x:x+j*2*(RADIUS+1)+(RADIUS+1),
110                     y:y+i*2*(RADIUS+1)+(RADIUS+1),
111                     g:1.5+Math.random(),
112                     vx:Math.pow( -1 , Math.ceil( Math.random()*1000 ) ) * 4,
113                     vy:-5,
114                     color: colors[ Math.floor( Math.random()*colors.length ) ]
115                 };
116                 balls.push(aBall);
117             }
118 }
119 
120 function getCurrentShowTimeSeconds() {
121     var curTime=new Date();
122     var ret=curTime.getHours()*3600+curTime.getMinutes()*60+curTime.getSeconds();
123     return ret;
124 }
125 
126 function render(cxt) {
127 
128     cxt.clearRect(0,0,WINDOW_WIDTH,WINDOW_HEIGHT);//对整个画布进行刷新,防止新画的跟以前画的叠加
129     var hours=parseInt(curShowTimeSeconds/3600);
130     var minutes=parseInt((curShowTimeSeconds-hours * 3600)/60);
131     var seconds=curShowTimeSeconds%60;
132 
133     renderDigit(MARGIN_LIFT,MARGIN_TOP,parseInt(hours/10),cxt);
134     renderDigit(MARGIN_LIFT+15*(RADIUS+1),MARGIN_TOP,parseInt(hours%10),cxt);
135     renderDigit(MARGIN_LIFT+30*(RADIUS+1),MARGIN_TOP,10,cxt);
136     renderDigit(MARGIN_LIFT+39*(RADIUS+1),MARGIN_TOP,parseInt(minutes/10),cxt);
137     renderDigit(MARGIN_LIFT+54*(RADIUS+1),MARGIN_TOP,parseInt(minutes%10),cxt);
138     renderDigit(MARGIN_LIFT+69*(RADIUS+1),MARGIN_TOP,parseInt(10),cxt);
139     renderDigit(MARGIN_LIFT+78*(RADIUS+1),MARGIN_TOP,parseInt(seconds/10),cxt);
140     renderDigit(MARGIN_LIFT+93*(RADIUS+1),MARGIN_TOP,parseInt(seconds%10),cxt);
141 
142     for(var i=0;i<balls.length;i++){
143         cxt.fillStyle=balls[i].color;
144 
145         cxt.beginPath();
146         cxt.arc(balls[i].x,balls[i].y,RADIUS,0,2*Math.PI,true);
147         cxt.closePath();
148 
149         cxt.fill();
150     }
151 
152 }
153 
154 function renderDigit(x,y,num,cxt) {
155     cxt.fillStyle="rgb(0,102,153)";
156 
157     for(var i=0;i<digit[num].length;i++)
158         for(var j=0;j<digit[num][i].length;j++)
159             if(digit[num][i][j]==1){
160             cxt.beginPath();
161             cxt.arc(x+j*2*(RADIUS+1)+(RADIUS+1),y+i*2*(RADIUS+1)+(RADIUS+1),RADIUS,0,2*Math.PI);
162             cxt.closePath();
163 
164             cxt.fill()
165             }
166 }

现在的时间,效果图。

至此,完成了canvas的动画基础!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏菩提树下的杨过

PixelBender(着色器)初体验

只要是玩过photoshop的人,一定会对ps中的各式各样、功能强大的滤镜(filter)留下深刻的印象。 Adobe是靠图形处理软件起家的,这方面一直是它的强...

29860
来自专栏码匠的流水账

聊聊spring cloud的AsyncLoadBalancerAutoConfiguration

本文主要研究一下AsyncLoadBalancerAutoConfiguration

13010
来自专栏菩提树下的杨过

发布一个轻量级的滑块控件

比系统自带的组件体积要小很多,而且支持进度条显示(在做播放器时,显示缓冲进度很有用哦),另外也支持三角形的音量调整显示 使用示例: package { imp...

39680
来自专栏移动开发

关于主线程中自动建立的Looper的思考:主线程中Looper中的轮询死循环为何没有阻塞主线程

Android中UI线程会自动给我们建立一个looper,但是looper中的loop方法是个死循环.为什么我们在UI线程中写的代码为何都能顺利执行?为什么没有...

25440
来自专栏racaljk

基于windows fiber的协程(coroutine)实现

一个非常简单,但是实用的协程实现,使用Windows的*Fiber函数族(linux可以稍微改一下用*context函数族)。

10810
来自专栏一个会写诗的程序员的博客

org.hamcrest.Matchers 类的方法签名

7520
来自专栏码云1024

c++ 平衡二叉树的实现

16140
来自专栏码匠的流水账

聊聊spring cloud的LoadBalancerAutoConfiguration

本文主要研究一下spring cloud的LoadBalancerAutoConfiguration

23620
来自专栏张泽旭的专栏

Java编写购物车系统

30940
来自专栏一个会写诗的程序员的博客

java.base.jmod

/Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home/jmods$ jmod list java....

18520

扫码关注云+社区

领取腾讯云代金券