`

canvas学习(三):炫丽的电子时钟

 
阅读更多

先来个效果图:

 效果很不错吧,学习视频链接如下:http://www.imooc.com/learn/133

(PS:老师讲的太好,忍不住为他打个小广告了,酷酷,强烈推荐去学习啊)

 

1、页面结构:跟前面两个一样的,body中一个canvas标签,id为myCanvas。另外引入digit.js和countdown.js。

 

2、digit.js:定义了一个名为digit的全局数组,记录了0-9以及冒号的11个点阵,一种1代表该处需要绘制一个小球,0处表示该处为空,不需要绘制东西。以下为0的点阵信息:

[
	[0,0,1,1,1,0,0],
	[0,1,1,0,1,1,0],
	[1,1,0,0,0,1,1],
	[1,1,0,0,0,1,1],
	[1,1,0,0,0,1,1],
	[1,1,0,0,0,1,1],
	[1,1,0,0,0,1,1],
	[1,1,0,0,0,1,1],
	[0,1,1,0,1,1,0],
	[0,0,1,1,1,0,0]
],//0

数组的下标从0开始,所以 digit数组也是从0开始,这样子digit[0]取的也就是0的点阵信息,digit[1]即为数字1的点阵信息,是不是很巧妙。

 

3、countdown.js:用来获取当前时间,根据当前时间和digit.js中的点阵信息以小球的形式显示电子钟,再根据时间的变化绘制上弹跳小球。代码如下:

var WINDOW_Width=1024;//canvas的宽度
var WINDOW_HEIGHT=600;//canvas的高度
var RADIUS=8;//圆球半径
var MAGIN_TOP=60;//每个数字距离画布上部的距离
var MAGIN_LEFT=30;//每个数字距离画布左部的距离
//var endTime = new Date(2016,5,30,18,00,00);//指定结束时间
//指定时间为当前时间一个小时之后
var endTime = new Date();
endTime.setTime(endTime.getTime()+1*60*60*1000);
var interval=null;
var curShowTimeSeconds=0;
//添加弹跳小球数据
var ball=[];//小球的坐标
var colors=["#33B5E5","#0099CC","#AA66CC","#9933CC","#99CC00","#669900","#FFBB33","#FF8800","#FF4444","#CC0000"];//小球的颜色
window.onload=function(){
	//1、获取myCanvas对象
	var	myCanvas = document.getElementById("myCanvas");
	//判断当前浏览器是否支持canvas
	if(myCanvas.getContext("2d")){
		var context = myCanvas.getContext("2d");//获取绘图上下文环境
		//通过获取屏幕的宽度和高度,给canvas设置宽高以自适应屏幕
		//此处有三个注意点:
		//1、必须给body和canvas标签添加style="height:100%;"的样式,让时间数字铺开整个屏幕;
		//2、必须使用document.documentElement.clientHeight,若是使用document.body.clientHeight获取到的高度非常小,显示的数字只能显示一半
		//3、必须减去20,否则会出现滚动条
		WINDOW_Width = document.documentElement.clientWidth-20;
		WINDOW_HEIGHT = document.documentElement.clientHeight-20;
		
		//设置宽高
		myCanvas.width=WINDOW_Width;
		myCanvas.height=WINDOW_HEIGHT;

		//设置margin-left和margin-top,以及RADIUS
		//10:时间数字占据整个屏幕的4/5,那么两边总共是1/5,一侧则是1/10
		MAGIN_LEFT = Math.round(WINDOW_Width/10);

		//时间数字占据整个屏幕的4/5,即window_width*4/5,这是所有时间数字占据的屏幕总宽度
		//而最后一个数字的左边距为margin_left+93*(radius+1),在加上它本身的15个(radius+1),因此这个4/5的屏幕宽度总共放置了93+15=108个(radius+1),计算出每个(radius+1)之后再减去1就是radius的值了
		RADIUS = Math.round(WINDOW_Width*4/5/108)-1;//108=93+15
		
		//margin_top跟上下文的坐标的计算没有关系,因此可以随便定义,此处定义为屏幕高度的1/5
		MAGIN_TOP = Math.round(WINDOW_HEIGHT/5);

		//alert(WINDOW_Width+"\n"+WINDOW_HEIGHT);

		//2、绘制当前时间图形
		curShowTimeSeconds = getCurrentShowTimeSeconds();
		setInterval(function(){
			render(context);//绘制数字和小球
			update();//更新数字和小球信息
		},50);//50毫秒执行一次
		
	}else{
		alert("当前浏览器不支持canvas标签");
	}
}

/**
*该方法用来绘制图形
*@param cxt:上下文绘图环境context
**/
function render(cxt){	
	if(curShowTimeSeconds>0){
		cxt.clearRect(0,0,WINDOW_Width,WINDOW_HEIGHT);//刷新整个canvas,不然时间数字和弹跳小球会堆积在整个画布中
		var hour = parseInt((curShowTimeSeconds/(60*60))%24,10);//小时  
		var minute = parseInt((curShowTimeSeconds/60)%60,10);//分钟:先计算成总共有多少分钟,再取分钟的余数  
		var seconds = curShowTimeSeconds%60;//秒:本来就是秒,直接获取最后剩下的秒就好了  
		
		//补零:当分钟和秒数只有两位数时,十位数/10时结果为零,会自动绘制0的图形,所以不再需要手动补零
		//minute = checkTime(minute);
		//seconds = checkTime(seconds);

		//时+冒号
		renderDigit(MAGIN_LEFT,MAGIN_TOP,parseInt(hour/10,10),cxt);//小时的十位数
		//15=7*2+1,7:每个点阵是7*10,每个方形的直径为2*(RADIUS+1),所以要乘以2,最后的1是保留空间,让两个数字之间有间距
		renderDigit(MAGIN_LEFT+(15*(RADIUS+1)),MAGIN_TOP,parseInt(hour%10,10),cxt);//小时的个位数
		//30:前面一个已经到15*(RADIUS+1),且每一个数字的间距都是15*(RADIUS+1),因此此处就到30了
		//10:digit点阵下标为10的,刚好是冒号对应的点阵,所以用10
		renderDigit(MAGIN_LEFT+(30*(RADIUS+1)),MAGIN_TOP,10,cxt);//小时后面的冒号

		//分+冒号
		//39=30+4*2+1:30是前一个冒号的x坐标,4是“冒号的点阵为4*10”,2为“方形的直径为2*(RADIUS+1)”,1为保留空间
		renderDigit(MAGIN_LEFT+(39*(RADIUS+1)),MAGIN_TOP,parseInt(minute/10,10),cxt);//分钟的十位数
		//54=39+15
		renderDigit(MAGIN_LEFT+(54*(RADIUS+1)),MAGIN_TOP,parseInt(minute%10,10),cxt);//分钟的个位数
		//69=54+15
		renderDigit(MAGIN_LEFT+(69*(RADIUS+1)),MAGIN_TOP,10,cxt);//分钟后面的冒号

		//秒+冒号
		//78=69+4*2+1
		renderDigit(MAGIN_LEFT+(78*(RADIUS+1)),MAGIN_TOP,parseInt(seconds/10,10),cxt);//秒数的十位数
		//93=78+15
		renderDigit(MAGIN_LEFT+(93*(RADIUS+1)),MAGIN_TOP,parseInt(seconds%10,10),cxt);//秒数的十位数

		//绘制小球
		for(var i=0;i<ball.length;i++){
			cxt.fillStyle=ball[i].color;//填充颜色
			cxt.beginPath();
			cxt.arc(ball[i].x,ball[i].y,RADIUS,0,2*Math.PI,true);//绘制弹跳小球
			cxt.closePath();
			cxt.fill();//给小球填充颜色
		}
	}
}

//获取当前时间
function getCurrentShowTimeSeconds(){
	/*(倒计时)
	var startTime = new Date().getTime();//开始时间  
    var diffTime = parseInt((endTime-startTime)/1000,10);//得到两个时间差的秒数
	return diffTime>0?diffTime:0;
	*/
	//时钟效果
	var curTime = new Date();//开始时间
	var diffTime = curTime.getHours()*60*60+curTime.getMinutes()*60+curTime.getSeconds();//得到两个时间差的秒数(电子钟报时)
	return diffTime;
}
function update(){
	var nextShowTimeSeconds = getCurrentShowTimeSeconds();
	var nextHour = parseInt((nextShowTimeSeconds/(60*60))%24,10);//小时  
	var nextMinute = parseInt((nextShowTimeSeconds/60)%60,10);//分钟:先计算成总共有多少分钟,再取分钟的余数  
	var nextSeconds = nextShowTimeSeconds%60;//秒:本来就是秒,直接获取最后剩下的秒就好了
	
	var curHour = parseInt((curShowTimeSeconds/(60*60))%24,10);//小时  
	var curMinute = parseInt((curShowTimeSeconds/60)%60,10);//分钟:先计算成总共有多少分钟,再取分钟的余数  
	var curSeconds = curShowTimeSeconds%60;//秒:本来就是秒,直接获取最后剩下的秒就好了  

	//因为秒数变化的比较最快,所以判断秒数是否相同
	if(nextSeconds!=curSeconds){
		//判断哪个数字发生了变化,若变化了就添加当前时间下的小球
		//小时
		if(parseInt(curHour/10)!=parseInt(nextHour/10)){
			addBall(MAGIN_LEFT+0,MAGIN_TOP,parseInt(curHour/10));//发生变化时,添加变化数字的弹跳小球
		}
		if(parseInt(curHour%10)!=parseInt(nextHour%10)){
			addBall(MAGIN_LEFT+(15*(RADIUS+1)),MAGIN_TOP,parseInt(curHour%10,10));
		}

		//分钟
		if(parseInt(curMinute/10)!=parseInt(nextMinute/10)){
			addBall(MAGIN_LEFT+(39*(RADIUS+1)),MAGIN_TOP,parseInt(curMinute/10,10));
		}
		if(parseInt(curMinute%10)!=parseInt(nextMinute%10)){
			addBall(MAGIN_LEFT+(54*(RADIUS+1)),MAGIN_TOP,parseInt(curMinute%10,10));
		}

		//秒
		if(parseInt(curSeconds/10)!=parseInt(nextSeconds/10)){
			addBall(MAGIN_LEFT+(78*(RADIUS+1)),MAGIN_TOP,parseInt(curSeconds/10,10));
		}
		if(parseInt(curSeconds%10)!=parseInt(nextSeconds%10)){
			addBall(MAGIN_LEFT+(93*(RADIUS+1)),MAGIN_TOP,parseInt(curSeconds%10,10));
		}

		curShowTimeSeconds = nextShowTimeSeconds;
	}

	//更新正在画布上运动的小球
	updateBalls();
	//console.log(ball.length);
}
//该方法用来处理正在画布上运动的小球
function updateBalls(){
	for(var i=0;i<ball.length;i++){
		ball[i].x +=ball[i].vx;
		ball[i].y +=ball[i].vy;
		ball[i].vy +=ball[i].g;
		
		//判断小球是否碰壁,若是碰壁就减小y方向的高度,达到小球越跳越低的效果
		if(ball[i].y>=WINDOW_HEIGHT-RADIUS){
			ball[i].y=WINDOW_HEIGHT-RADIUS;
			ball[i].vy = -ball[i].vy*0.75;//慢慢降低弹跳小球的高度,达到小球越跳越低的效果
		}
	}

	//判断小球是否还在画布中
	var cnt = 0;
	for(var i=0;i<ball.length;i++){
		if(ball[i].x+RADIUS>0 && ball[i].x-RADIUS<WINDOW_Width){//说明小球还在画布中
			ball[cnt++]=ball[i];//将在画布中的小球全部添加到ball的前面
		}
	}
	//通过前面for循环的处理,在画布中的小球全部集中到了ball数组的全面,那么后面部分即为不在屏幕中的小球,可以清空掉
	//按道理while循环中的条件应该为:ball.length>cnt,这样子会有一个bug,就是:打开该页面以后,切换到别的页面,弹跳的数量会一直积累,达不到优化性能的效果
	//所以修改为ball.length>Math.min(300,cnt),让画布中的弹跳小球保持在300附近
	while(ball.length>Math.min(300,cnt)){
		//pop() 方法用于删除并返回数组的最后一个元素。
		ball.pop();//将数组末尾的小球删掉,避免内存中小球数量太多影响性能
	}

}

	/**
	*该方法用来添加弹跳的小球
	*@param x:小球起点x坐标
	*@param y:小球起点y坐标
	*@param num:需要添加弹跳小球的数字
	**/
function addBall(x,y,num){
	for(var i=0;i<digit[num].length;i++){
		//循环点阵中的各个点
		for(var j=0;j<digit[num][i].length;j++){
			if(digit[num][i][j]==1){//此处以小球形式显示,需要绘制一个弹跳圆球
				var aBall={
					x:(x+j*2*(RADIUS+1)+(RADIUS+1)),//x坐标
					y:(y+i*2*(RADIUS+1)+(RADIUS+1)),//y坐标
					g:1.5+Math.random(),//加速度:设置为随机数,到小球弹跳不规则
					vx:Math.pow(-1,Math.ceil(Math.random()*1000))*4,//小球在x方向的速度(-4或者+4)
					vy:-5,//小球在y方向的速度,让小球在生成时有一个向上抛的动作
					color:colors[Math.floor(Math.random()*colors.length)]//小球的颜色
				};
				ball.push(aBall);//将生成的小球添加到ball数组中
			}
		} 
	}
}
/** 
*该方法用来解决“分秒为1位数以下时自动添加零变成2位数”的问题 
*@param value:需要补零的值 
*/  
function checkTime(value){  
    value = parseInt(value,10);  
    if(value<10){  
        value = "0"+value;  
    }  
    return value;  
}

/**
*该方法用来绘制图形
*@param x:起点x坐标
*@param y:起点y坐标
*@param num:需要进行绘制的数字
*@param cxt:上下文绘图环境context
**/
function renderDigit(x,y,num,cxt){
	cxt.fillStyle="rgb(0,102,153)";
	//循环指定数字的点阵
	for(var i=0;i<digit[num].length;i++){
		//循环点阵中的各个点
		for(var j=0;j<digit[num][i].length;j++){
			if(digit[num][i][j]==1){//需要绘制一个圆球
				cxt.beginPath();
				//RADIUS为圆球的半径,RADIUS+1作为盛放圆球的方形半径,1是多余出来的空间,那么方形的直径为:2*(RADIUS+1)
				//x+j*2*(RADIUS+1)+(RADIUS+1):x+j*2*(RADIUS+1)计算要绘制的球形所在前一个方形的位置,
				//(RADIUS+1)为下一个方形的一半,也就是要绘制的球形的中心
				cxt.arc((x+j*2*(RADIUS+1)+(RADIUS+1)),(y+i*2*(RADIUS+1)+(RADIUS+1)),RADIUS,0,2*Math.PI);//默认顺时针
				cxt.closePath();
				cxt.fill();//绘制
			}
		}
	}
}

具体实例已经上传到附件,有需要的可以下载。记得在支持html5的浏览器中执行哦!

  • 大小: 82.4 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics