用js玩桌球游戏

有击球、进球效果、力度提示、击球方向提示

用到的图片素材

<!doctype html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <meta name="Generator" content="EditPlus®">
  <meta name="Author" content="">
  <meta name="Keywords" content="">
  <meta name="Description" content="">
  <title>Document</title>
  <style>
		body{margin:0;padding:0;}
		.view{width: 800px;height: 544px;margin:20px auto;position: relative;}
		#cas{display: block;border:1px solid;background:url(table.jpg) no-repeat;}
		#shotPower{width: 30px;height: 80px;position: absolute;right: -80px;top: 150px;display: none}
		#powbar{width: 5px;height: 60px;margin:auto;background-color: #CCC;position: relative;}
		#pow{width: 100%;height:50%;position: absolute;bottom: 0px;}
		#maxPower{color: #FFF;text-align: center;width: 100%;padding-bottom:3px;font-size: 10px;}
		#powText{color: #FFF;text-align: center;width: 100%;padding-top: 3px;font-size: 8px;}
		.animate{-webkit-animation:add 2s infinite linear;}
		@-webkit-keyframes add{
			0%{height:0%;background-color:#ff0;}
			50%{height:100%;background-color:#f00;}
			100%{height:0%;background-color:#ff0;}
		}
	</style>
 </head>
 <body style="background-color: #000" onselectstart = "return false";>
	<div class="view">
		<canvas id="cas" width="800" height="544">您的浏览器不支持canvas,请升级浏览器</canvas>
		<img src="white_ball.png" id="wb" alt="" style="display:none"/>
		<img src="yellow_ball.png" id="yb" alt="" style="display:none"/>
		<div id="shotPower">
			<div id="maxPower">MAX</div>
			<div id="powbar"><div id="pow"></div></div>
			<div id="powText">power</div>
		</div>
		<div style="position:absolute;left:-100px;top:250px;color:#FFF">进球数:<span id="shotNum">0</span></div>
	</div>
 </body>
 <script>
	var canvas=document.getElementById('cas'),
		cw=canvas.width,
		ch=canvas.height,
		ctx=canvas.getContext('2d'),
		f=0.009,
		radius=15,
		wball=document.getElementById('wb'),
		yball=document.getElementById('yb'),
		balls=[],
		start=0,
		isMoving=false,
		mousedown=false,
		showPower=document.getElementById('shotPower'),
		pow=document.getElementById('pow'),
		powbar=document.getElementById('powbar'),
		line,
		certs=document.getElementById('shotNum');

	Array.prototype.forEach=function(callback){
		for(var i=0;i<this.length;i++){
			callback.call(this[i]);
		}
	}

	canvas.onmousedown=function(event){
		mousedown=true;
		if(!isMoving){/*没有球在运动,才响应点击事件*/
			line.display=true;
			/*根据母球的位置设置线条的起始位置,根据鼠标点击位置计算线条的结束位置*/
			line.x0=balls[0].x;
			line.y0=balls[0].y;
			line.x1=event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft - document.querySelector(".view").offsetLeft;
			line.y1=event.clientY + document.body.scrollTop + document.documentElement.scrollTop - document.querySelector(".view").offsetTop;

			/*显示力度条*/
			shotPower.style.display='block';
			shotPower.style.left=balls[0].x-radius*3+'px';
			showPower.style.top=balls[0].y-60+'px';
			pow.classList.add('animate');
		}

		document.onmousemove=function(event){
			if(mousedown && !isMoving){/*没有球在运动,才响应点击事件,根据鼠标移动位置不断改变线条的结束位置*/
				line.x1=event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft - document.querySelector(".view").offsetLeft;
				line.y1=event.clientY + document.body.scrollTop + document.documentElement.scrollTop - document.querySelector(".view").offsetTop;
			}
		}

		document.onmouseup=function(event){
			var rate=pow.offsetHeight/powbar.offsetHeight;/*根据鼠标抬起瞬间,力度条里两div的高度比来模拟*/
			var speed=rate*60;
			balls[0].vx=speed*line.rateX;
			balls[0].vy=speed*line.rateY;
			balls[0].isMoving=true;
			isMoving=true;
			
			shotPower.style.display='none';
			line.display=false;
		}
	}
	var Line=function(x0,y0,x1,y1){
		this.x0=x0;
		this.y0=y0;
		this.x1=x1;
		this.y1=y1;
		this.dotLength=3;
		this.display=false;
	}
	Line.prototype={
		constructor:Line,
		calc:function(){
			this.length=Math.sqrt(Math.pow(this.x0-this.x1,2)+Math.pow(this.y0-this.y1,2));
			this.dotCount=Math.ceil(this.length/this.dotLength);
			this.rateX=(this.x1-this.x0)/this.length;
			this.rateY=(this.y1-this.y0)/this.length;
		},
		paint:function(){
			this.calc();
			var xadd=this.dotLength*this.rateX;
			var yadd=this.dotLength*this.rateY;
			ctx.save();
			ctx.beginPath();
			ctx.strokeStyle='#fff';
			/*画出虚线*/
			for(var i=0;i<this.dotCount;i++){
				if(i%2==0){
					ctx.moveTo(this.x0+xadd*i,this.y0+yadd*i);
					ctx.lineTo(this.x0+xadd*(i+1),this.y0+yadd*(i+1));
					ctx.stroke();
				}
			}
			/*画出跟随鼠标的球*/
			ctx.arc(this.x1,this.y1,radius-3,0,Math.PI*2);
			ctx.stroke();
			ctx.closePath();
			ctx.restore();
		}
	}
	var Ball=function(x,y,radius,flag){
		this.x=x;
		this.y=y;
		this.radius=radius;
		this.vx=0;
		this.vy=0;
		this.isBoss=flag;
		this.isMoving=false;
		this.isIn=false;
		this.paint();
	}
	Ball.prototype={
		constructor:Ball,
		paint:function(){
			var b=this.isBoss?wball:yball;
			/*把图片画到画布上之前,确保图片已经加载完*/
			if(b.complete){
				ctx.drawImage(b,this.x-radius,this.y-radius,radius*2,radius*2);
			}else{
				b.onload=function(){
					ctx.drawImage(b,this.x-radius,this.y-radius,radius*2,radius*2);
				}
			}
		},
		update:function(t){
			if(this.isMoving){/*只对运动中的球改变速度、位置,检测是否入袋、碰到桌面边缘;只要有球在动,全局运动状态一定为true*/
				isMoving=true;/*改变全局运动状态*/
				
				this.vx-=this.vx*(this.vx*f*t)>0?this.vx*f*t:this.vx;
				this.vy-=this.vy*(this.vy*f*t)>0?this.vy*f*t:this.vy;
				
				/*运动中的球停止运动的条件*/
				this.vx=Math.abs(this.vx)>0.1?this.vx:0;
				this.vy=Math.abs(this.vy)>0.1?this.vy:0;
				if(this.vx==0 && this.vy==0){
					this.isMoving=false;
				}else{
					this.isMoving=true;
				}

				this.x+=this.vx*t;
				this.y+=this.vy*t;

				if((this.x<55 && this.y<55) || (this.x>370 && this.x<430 && this.y<50) || (this.x > 740 && this.y<55) || (this.x<46 && this.y>490) || (this.x>377 && this.x<420 && this.y>490) || (this.x > 740 && this.y>490)){/*球入袋条件*/
					this.vx=0;
					this.vy=0;
					this.isMoving=false;
					this.isIn=true;				
					
					if(this.isBoss){/*母球入袋,就把它放回初始位置*/
						this.x=201;
						this.y=ch/2;
						this.isIn=false;
					}else{
						certs.innerHTML=parseInt(certs.innerHTML)+1;/*不是母球入袋计数加1*/
					}
				}else{
					if(this.x<32+this.radius || this.x>canvas.width-32-this.radius){/*x方向*/
						this.x=this.x<32+this.radius?32+this.radius:canvas.width-32-this.radius;/*拉回边界*/
						this.vx=-this.vx*0.95;/*改变方向*/
					}
					if(this.y<32+this.radius || this.y>canvas.height-32-this.radius){/*y方向*/
						this.y=this.y<32+this.radius?32+this.radius:canvas.height-32-this.radius;/*拉回边界*/
						this.vy=-this.vy*0.95/*改变方向*/
					}
				}
			}
			this.paint();/*还是要继续画出每一个球*/
		}
	}

	function createBall(){
		var boss=new Ball(202,ch/2,radius,true);
		balls.push(boss);
		for(var i=0;i<6;i++){
			for(var j=0;j<i;j++){
				var ball=new Ball(520+radius*2*i,ch/2-( (i+1)*radius*2/2-j*2*radius )+2*radius,radius,false );
				balls.push(ball);
			}
		}
	}

	function collisionTest(){
		var ballCount=balls.length;
		for(var i=0;i<ballCount;i++){
			for(var j=i+1;j<ballCount;j++){
				var b1=balls[i];
				var b2=balls[j];
				var rc;
				if(b1.isIn || b2.isIn || (!b1.isMoving && !b2.isMoving) ) continue;/*检测的两球中有任意一球入袋,或者两球都没动,就不用检测碰撞了*/
				rc=Math.sqrt(Math.pow(b1.x-b2.x,2)+Math.pow(b1.y-b2.y,2));
				if(2*radius>Math.ceil(rc)){
					b1.isMoving=b2.isMoving=true;/*碰撞后确保两球的运动状态都为true*/
					var ax = ((b1.vx - b2.vx)*Math.pow((b1.x - b2.x) , 2) + (b1.vy - b2.vy)*(b1.x - b2.x)*(b1.y - b2.y))/Math.pow(rc , 2)
					var ay = ((b1.vy - b2.vy)*Math.pow((b1.y - b2.y) , 2) + (b1.vx - b2.vx)*(b1.x - b2.x)*(b1.y - b2.y))/Math.pow(rc , 2)
					b1.vx = b1.vx-ax;
					b1.vy = b1.vy-ay;
					b2.vx = b2.vx+ax;
					b2.vy = b2.vy+ay;

					/*碰撞后两球的位置修正*/
					var clength = ((b1.radius+b2.radius)-rc)/2;
					var cx = clength * (b1.x-b2.x)/rc;
					var cy = clength * (b1.y-b2.y)/rc;
					b1.x = b1.x+cx;
					b1.y = b1.y+cy;
					b2.x = b2.x-cx;
					b2.y = b2.y-cy;
				}
			}
		}
	}

	function animate(){
		var now=+new Date();
		if(now-start>30){
			//console.log(now-start);
			start=now;
			if(isMoving){/*有球在动才做碰撞检测,下一个动画前清空画布、并重置全局运动状态为false,在重画所有球的过程中同时检测全局运动状态是否改变*/
				collisionTest();
				ctx.clearRect(0,0,cw,ch);
				isMoving=false;/*先重置全局运动状态,检查看下面的循环能不能改变该状态,如果改变了说明还在动*/
				balls.forEach(function(){
					if(!this.isIn)/*如果球入袋,不要做任何操作,所以在这里判断;如果球没动,动画中还需要重新画出,所以在这里判断*/
						this.update(1);
				});
				line.display=false;/*确保球运动过程中不会显示线条*/
			}else{
				if(line.display){/*画线条时也要清空画布,重画所有桌球;反之不用重画线条、球静全部止时,什么都不用重画*/
					ctx.clearRect(0,0,cw,ch);
					line.paint();
					balls.forEach(function(){
						if(!this.isIn)
							this.paint();
					});
				}
			}
		}
		if('requestAnimationFrame' in window){
			requestAnimationFrame(animate);
		}else if('webkitRequestAnimationFrame' in window){
			webkitRequestAnimationFrame(animate);
		}else if('mozRequestAnimationFrame' in window){
			mozRequestAnimationFrame(animate);
		}
	}
	function init(){
		line=new Line(202,ch/2,0,0);/*线条初始化*/
		createBall();/*桌球初始化*/
		start=+new Date();
		animate();/*运动初始化*/
	}
	setTimeout(function(){init();},1000);
 </script>
</html>

 

转载于:https://my.oschina.net/codespring/blog/772137

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值