前言
想要在自己做的网页中,加入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的动画基础!