有击球、进球效果、力度提示、击球方向提示
用到的图片素材
<!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>