您现在的位置:首页 >> 前端 >> 内容

canvas绘图,倒计时特效时钟

时间:2017/4/11 9:20:00 点击:

  核心提示:canvas绘图,倒计时特效时钟:参数为html文档上画布canvas的id,必须有canvas元素,把canvas的id填入此处,作为参数传入。function clock(canvas_id){ ...
canvas绘图,倒计时特效时钟:参数为html文档上画布canvas的id,必须有canvas元素,把canvas的id填入此处,作为参数传入。
function clock(canvas_id){
	!function(canvas_id){
	"begin of api";
		var width = 1000;//绘制主要以width为参考来决定弹球半径
		var height = 200;//height只用来计算绘制数字的基线
		var margin_horiz = 150;//水平方向的时钟绘制区域与左右两个边界的保留距离
		var margin_vertic = 50;//暂时不用,只是保留下来
		var gapCalcByRad = 1.0;//数字位之间的间隙
		var median = height / 2;//基线高度
		var ball_radius = (width - 2 * margin_horiz) / (100 + 14.0 * gapCalcByRad);//弹球圆球的半径
		var fillRem = 0.9;//弹球,圆的绘制半径,实际上只填充圆的0.8个半径范围

		var lossFactor = 0.6;//速度的损益因子,每次碰撞后速度为原来的lossFactor倍,保持位正数,切不能大于1
		const deltaTime = 80;//刷新时间, 以毫秒计, 影响帧率

		var countdown_switch = true;//倒计时开关,设置为true表示使用倒计时功能,false时,只作为时钟显示时间
		var destTime = undefined;//用于倒计时的目标时间
		setDestTime(2016, 11, 2, 0, 7, 7);//1-12月,从1开始计数,屏蔽了Date对象的从0开始计数
		//目标时间跟现在的时间的 差值,不暴露,但和destTime放在一起,方便查找
		//当不适用倒计时功能时,设置为现在的时间,绘制钟表
		var diffTime = undefined;//看上面两行注释
		//偏好的颜色预置选项
		const colors = ["#33B5E5","#0099CC","#AA66CC","#9933CC","#99CC00","#669900","#FFBB33","#FF8800","#FF4444","#CC0000"];
		var digit_color = colors[5];//绘制数位的颜色
		//开启
		function start(){
			//定时刷新
			setInterval(function(){
			updateClock();
			updateMarbles();
			addMarbleArray();
			renderClock();
			renderMarbles();
			}, deltaTime);
		};
	"end of api";

	//以下数据不暴露
		const $2PI = 2 * Math.PI;
		var digit =
		    [
		        [
		            [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,0,0,1,1,0,0],
		            [0,1,1,1,1,0,0],
		            [0,0,0,1,1,0,0],
		            [0,0,0,1,1,0,0],
		            [0,0,0,1,1,0,0],
		            [0,0,0,1,1,0,0],
		            [0,0,0,1,1,0,0],
		            [0,0,0,1,1,0,0],
		            [0,0,0,1,1,0,0],
		            [1,1,1,1,1,1,1]
		        ],//1
		        [
		            [0,1,1,1,1,1,0],
		            [1,1,0,0,0,1,1],
		            [0,0,0,0,0,1,1],
		            [0,0,0,0,1,1,0],
		            [0,0,0,1,1,0,0],
		            [0,0,1,1,0,0,0],
		            [0,1,1,0,0,0,0],
		            [1,1,0,0,0,0,0],
		            [1,1,0,0,0,1,1],
		            [1,1,1,1,1,1,1]
		        ],//2
		        [
		            [1,1,1,1,1,1,1],
		            [0,0,0,0,0,1,1],
		            [0,0,0,0,1,1,0],
		            [0,0,0,1,1,0,0],
		            [0,0,1,1,1,0,0],
		            [0,0,0,0,1,1,0],
		            [0,0,0,0,0,1,1],
		            [0,0,0,0,0,1,1],
		            [1,1,0,0,0,1,1],
		            [0,1,1,1,1,1,0]
		        ],//3
		        [
		            [0,0,0,0,1,1,0],
		            [0,0,0,1,1,1,0],
		            [0,0,1,1,1,1,0],
		            [0,1,1,0,1,1,0],
		            [1,1,0,0,1,1,0],
		            [1,1,1,1,1,1,1],
		            [0,0,0,0,1,1,0],
		            [0,0,0,0,1,1,0],
		            [0,0,0,0,1,1,0],
		            [0,0,0,1,1,1,1]
		        ],//4
		        [
		            [1,1,1,1,1,1,1],
		            [1,1,0,0,0,0,0],
		            [1,1,0,0,0,0,0],
		            [1,1,1,1,1,1,0],
		            [0,0,0,0,0,1,1],
		            [0,0,0,0,0,1,1],
		            [0,0,0,0,0,1,1],
		            [0,0,0,0,0,1,1],
		            [1,1,0,0,0,1,1],
		            [0,1,1,1,1,1,0]
		        ],//5
		        [
		            [0,0,0,0,1,1,0],
		            [0,0,1,1,0,0,0],
		            [0,1,1,0,0,0,0],
		            [1,1,0,0,0,0,0],
		            [1,1,0,1,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],
		            [0,1,1,1,1,1,0]
		        ],//6
		        [
		            [1,1,1,1,1,1,1],
		            [1,1,0,0,0,1,1],
		            [0,0,0,0,1,1,0],
		            [0,0,0,0,1,1,0],
		            [0,0,0,1,1,0,0],
		            [0,0,0,1,1,0,0],
		            [0,0,1,1,0,0,0],
		            [0,0,1,1,0,0,0],
		            [0,0,1,1,0,0,0],
		            [0,0,1,1,0,0,0]
		        ],//7
		        [
		            [0,1,1,1,1,1,0],
		            [1,1,0,0,0,1,1],
		            [1,1,0,0,0,1,1],
		            [1,1,0,0,0,1,1],
		            [0,1,1,1,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],
		            [0,1,1,1,1,1,0]
		        ],//8
		        [
		            [0,1,1,1,1,1,0],
		            [1,1,0,0,0,1,1],
		            [1,1,0,0,0,1,1],
		            [1,1,0,0,0,1,1],
		            [0,1,1,1,0,1,1],
		            [0,0,0,0,0,1,1],
		            [0,0,0,0,0,1,1],
		            [0,0,0,0,1,1,0],
		            [0,0,0,1,1,0,0],
		            [0,1,1,0,0,0,0]
		        ],//9
		        [
		            [0,0,0,0],
		            [0,0,0,0],
		            [0,1,1,0],
		            [0,1,1,0],
		            [0,0,0,0],
		            [0,0,0,0],
		            [0,1,1,0],
		            [0,1,1,0],
		            [0,0,0,0],
		            [0,0,0,0]
		        ]//:
		    ];
		
		//数字位的锚点,以此为中心绘制数字点阵
		var anchors = [];
		//画布
		var canvas = undefined;
		//画布的设备上下文,内涵绘制图形的接口
		var ctx = undefined;


		//还要绘制的digit,包括冒号
		var charListToRender = [];
		//上一次绘制的digit,用于和新digit作比较
		var expiredCharList = [];
		//比较charListToRender expiredCharList的不同之处,并把不同之处的索引存放在crackedNumberIndex中
		//据此来绘制跳动的小球
		var crackedNumberIndex = [];
		//要绘制的彩色小球,保存在数组里
		var marbles = [];
		//定义一个marble类构造器
		function marble(posx, posy, vx, vy, color){
			this.positionX = posx;
			this.positionY = posy;
			this.velocityOnX = vx;
			this.velocityOnY = vy;
			this.color = color;
			this.update = function(){
				this.positionX += this.velocityOnX * this.$deltaTime + this.accelOnX * this.$halfSqureTime;//s = a*t*t/2 + vt
				this.positionY += this.velocityOnY * this.$deltaTime + this.accelOnY * this.$halfSqureTime;
				//this.velocityOnX += this.accelOnX * this.$deltaTime;
				this.velocityOnY += this.accelOnY * this.$deltaTime;
				if(this.positionY > this.bottomLimit){
					this.positionY = 2 * height - this.positionY;
					this.velocityOnY *= (Math.abs(this.velocityOnY) > 30 ? -this.lossFactor : 1);
				}
				else if(this.positionY < this.topLimit){
					this.positionY = -this.positionY;
					this.velocityOnY *= -this.lossFactor;
				}
			};
		};
		//marble的原型,可以通过某种方式暴露出去,让用户修改,当然要在开始循环绘制之前才会有效果
		marble.prototype.accelOnX = 0.0;//x方向的加速度,向右为正
		marble.prototype.accelOnY = 50.0;//y轴加速度,向下为正
		marble.prototype.maxInitVelX = 65;//x轴方向最大初始速度绝对值,
		marble.prototype.minInitVelX = 40;//x轴方向最小初始速度绝对值
		marble.prototype.maxInitVelY = 65;//y轴方向最大初始速度绝对值
		marble.prototype.minInitVelY = 40;//y轴方向最小初始速度绝对值
		marble.prototype.topLimit = 0;//顶部边界,超出边界的小球被弹回
		marble.prototype.bottomLimit = height;//底部边界
		marble.prototype.leftLimit = 0;//左边界
		marble.prototype.rightLimit = width;//右边界
		marble.prototype.$deltaTime = deltaTime / 1000;//刷新时间,以秒为单位
		marble.prototype.$halfSqureTime = 0.5 * (deltaTime * deltaTime / 1000 / 1000);//a*t*t/2,单独保存,节省计算量
		marble.prototype.horiz_dir_diff_switch_off = false;//是否关闭弹球在水平方向上随即向左右两边分流
		marble.prototype.vertical_dir_diff_switch_off = true;//是否关闭弹球在垂直方向上的初始速度上下分流
		marble.prototype.lossFactor = lossFactor;//发生碰撞时,速度的折损系数
		//网页打开时候回调函数
		window.onload = function(){
			canvas = document.getElementById(canvas_id);
			ctx = canvas.getContext("2d");
			canvas.width = width;
			canvas.height = height;

			var acc = margin_horiz; 
			//计算数字位的锚点,以此为中心绘制数字
			for(let i = 0; i< 8; i++){
				if( i == 2 || i == 5){
					anchors[i] = (acc + 4.0 * ball_radius + gapCalcByRad * ball_radius);
					acc += 8.0 * ball_radius + gapCalcByRad * ball_radius;
				}
				else{
					anchors[i] = (acc + 7.0 * ball_radius + gapCalcByRad * ball_radius);
					acc += 14.0 * ball_radius + gapCalcByRad * ball_radius;
				}
			}
			ctx.fillStyle = digit_color;
			start();
		};
		//绘制弹球
		function renderMarbles(){
			let length = marbles.length;
			let radius = ball_radius;
			for(let i = 0; i < length; i++){
				drawBall(radius, marbles[i].positionX, marbles[i].positionY, marbles[i].color);
			}
		}
		//计算出变成marbles的数字为的索引,存放于crackedNumberIndex数组
		function updateClock(){
			expiredCharList = charListToRender;
			getCharListToRender();
			var length = expiredCharList.length;
			crackedNumberIndex.length = 0;//防止内存溢出
			for(let i = 0; i< length; i++){
				if(expiredCharList[i] != charListToRender[i]){
					crackedNumberIndex.push(i);
				}
			}
		}
		//遍历更新弹球的状态,包括速度,位置等
		function updateMarbles(){
			let length = marbles.length;
			for(let i = 0; i < length; i++){
				marbles[i].update();
			}
		}
		//每次数字位发生变化时会增加弹球数量
		function addMarbleArray(){
			let length = crackedNumberIndex.length, i = 0, j = 0;
			let colorsCount = colors.length;
			let maxInitVelX = marble.prototype.maxInitVelX;
			let maxInitVelY = marble.prototype.maxInitVelY;
			let minInitVelX = marble.prototype.minInitVelX;
			let minInitVelY = marble.prototype.minInitVelY;
			let horiz_dir_diff_switch_off = marble.prototype.horiz_dir_diff_switch_off;
			let vertical_dir_diff_switch_off = marble.prototype.vertical_dir_diff_switch_off;
			//marbles.length = 0;//重置数组,防止内存溢出
			for(let index = 0; index < length; index++){
				//如果不是两个冒号符号位,事实上冒号位发生变化不可能的,index是存放改变的数位的数组的索引,
				//number得到clock上变化的数字位的索引
				let number = crackedNumberIndex[index];
				if(number != 2 && number != 5){
					for(i = 0; i < 10; i++){
						for(j = 0; j < 7; j++){
							if(digit[expiredCharList[number]][i][j] == 1){
								marbles.push(new marble(2 * (j-3.0)*ball_radius+anchors[number], 
									2 * (i-4.5)*ball_radius+median, 
									(Math.random() * (maxInitVelX - minInitVelX) + minInitVelX) * (horiz_dir_diff_switch_off ? -1 : (Math.random() * 2 > 1 ? 1 : -1)),
									(Math.random() * (maxInitVelY - minInitVelY) + minInitVelY) * (vertical_dir_diff_switch_off ? -1 : (Math.random() * 2 > 1 ? 1 : -1)),
									colors[Math.ceil(Math.random() * colorsCount)]
									));
							}
						}
					}
				}else{
					alert("冒号位不应发生变化!");
				}
			}
			if(marbles.length > 1000){
				for( let i = 0; i < marbles.length - 1000; i++){
					marbles.shift()
				}
			}
		}
		//绘制时钟数字
		function renderClock(){
			ctx.clearRect(0, 0, canvas.width, canvas.height);
			for(let i = 0; i< charListToRender.length; i++){
				drawChar(charListToRender[i], anchors[i], median);
			}
		}
		//计算要绘制的符号,考虑到是否禁用倒计时功能
		function getCharListToRender(){
			var diffTime = (countdown_switch ? (parseInt((destTime.getTime() - new Date().getTime()))) : (new Date().getTime() - new Date(0, 0, 0)) )  / 1000;//以秒计数
			var diffHours = parseInt(diffTime / 3600);
			var diffMinutes = parseInt((diffTime % 3600) / 60);
			var diffSeconds = parseInt(diffTime % 60);

			if(diffTime < 0.005){
				charListToRender = [0,0,10,0,0,10,0,0];
				return;
			}
			//小时数超过两位数,就会出问题,做10进制求余运算,解决这种情况
			charListToRender = [
				countdown_switch ? parseInt(diffHours/10%10) : parseInt(diffHours%24/10), 
				countdown_switch ? parseInt(diffHours%10) : parseInt(diffHours%24%10), 
				10, 
				parseInt(diffMinutes/10), 
				parseInt(diffMinutes%10), 
				10, 
				parseInt(diffSeconds/10), 
				parseInt(diffSeconds%10)];
		}
		//可以暴露的接口,用于设置目标时间,用于倒计时
		function setDestTime(year, month=1, day=1, hour=0, minute=0, second=0){
			destTime = new Date(year, month - 1, day, hour , minute, second);
		}
		//绘制数字位,通过调用drawBall函数实现
		function drawChar(index, x, y){
			let i = 0, j = 0;
			let radius = ball_radius * fillRem;
			if(index != 10){
				for(i = 0; i < 10; i++){
					for(j = 0; j < 7; j++){
						if(digit[index][i][j] == 1){
							drawBall(radius, 2 * (j-3.0)*ball_radius+x, 2 * (i-4.5)*ball_radius+y);
						}
					}
				}
			}else{
				for(i = 0; i < 10; i++){
					for(j = 0; j < 4; j++){
						if(digit[index][i][j] == 1){
							drawBall(radius, 2 * (j-1.5)*ball_radius+x, 2 * (i-4.5)*ball_radius+y);
						}
					}
				}
			}
		}
		//绘制圆球
		function drawBall(radius, x, y, color = digit_color){
			ctx.save();
			ctx.fillStyle = color;
			ctx.beginPath();
			ctx.arc(x, y, radius, 0, $2PI)
			ctx.closePath();
			ctx.fill();
			ctx.restore();
		}
	}(canvas_id);//参数为html文档上画布canvas的id,必须有canvas元素,把canvas的id填入此处,作为参数传入
}

Tags:CA AN NV VA 
作者:网络 来源:不详