<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta charset="utf-8">
<title>小球碰撞</title>
<style type="text/css">
.center{width:400px;text-align: center;margin: 0 auto;}
#wrapper{position:relative;width:400px;height:400px;box-shadow: 0px 0px 3px #333 inset;background: #666;}
.ball{position:absolute;width:20px;height:20px;border-radius: 10px;background:#fff;box-shadow: 2px 2px 2px #666;visibility: hidden;}
</style>
<script type="text/javascript">
//下次加小球碰撞
var speed = 5;
var seq = 0;
var boundary = null;
var interval;
var balls = [];
var colors = ['rgba(0,0,240,0.8)', 'rgba(0,0,240,0.7)', 'rgba(0,0,240,0.6)', 'rgba(0,0,240,0.5)', 'rgba(0,0,240,0.4)', 'rgba(0,0,240,0.3)', 'rgba(0,0,240,0.2)'];

var GeneralObject = function(){
this.x = null;
this.y = null;
this.width = 0;
this.height = 0;
this.dom = null;
};

function Ball(id){
this.dom = document.getElementById(id);
this.x = null;
this.y = null;
this.width = this.dom.offsetWidth;
this.height = this.dom.offsetHeight;
this.hspeed = 2*speed * (Math.floor(Math.random() * 10) + 1)/10;
this.vspeed = 2*speed * (Math.floor(Math.random() * 10) + 1)/10;
};
Ball.prototype = new GeneralObject();

function Boundary(id){
this.dom = document.getElementById(id);
this.width = this.dom.offsetWidth;
this.height = this.dom.offsetHeight;

};
Boundary.prototype = new GeneralObject();

function init(){
var ball = new Ball('ball');
boundary = new Boundary('wrapper');
ball.dom.style.left = Math.random()*(boundary.width - ball.width) + 'px';
ball.x = parseInt(ball.dom.style.left);
ball.dom.style.top = Math.random()*(boundary.height - ball.height) + 'px';
ball.y = parseInt(ball.dom.style.top);
ball.dom.style.visibility = 'visible';
balls.push(ball);

interval = setInterval(function(){
go(balls, boundary);
}, 20);

}

function go(balls, boundary){
for(var i = 0; i < balls.length; i++){
balls[i].dom.style.left = balls[i].x + balls[i].hspeed + 'px';
balls[i].x = parseInt(balls[i].dom.style.left);
balls[i].dom.style.top = balls[i].y + balls[i].vspeed + 'px';
balls[i].y = parseInt(balls[i].dom.style.top);

hitTest(balls[i], boundary);
//得保证生成小球的地方没有已存在的小球与之重叠,现在还没有做到
// if(i >= 1){
// for(var j = i-1; j >= 0; j--){
// hitBall(balls[i], balls[j]);
// }
// }

}
}

function hitTest(ball, boundary){
if(ball.y + ball.height >= boundary.height || ball.y + ball.height + ball.vspeed > boundary.height){
ball.vspeed = -ball.vspeed;
}
if(ball.x + ball.width >= boundary.width || ball.x + ball.width + ball.hspeed > boundary.width){
ball.hspeed = -ball.hspeed;
}
if(ball.y <= 0 || ball.y + ball.vspeed < 0){
ball.vspeed = -ball.vspeed;
}
if(ball.x <= 0 || ball.x + ball.hspeed < 0){
ball.hspeed = -ball.hspeed;
}
}

function hitBall(ballA, ballB){
if(CheckIntersect(ballA, ballB)){
//简单处理为交换速度
var temp;
temp = ballA.vspeed;
ballA.vspeed = ballB.vspeed;
ballB.vspeed = temp;
temp = ballA.hspeed;
ballA.hspeed = ballB.hspeed;
ballB.hspeed = temp;
}
}

//圆形碰撞检测与方形碰撞检测原理不一样
function CheckIntersect(ball1, ball2){
var p1 = {};//提取出去作为ball的属性,在这个被循环调用的函数里面新建对象不好
p1.x = ball1.x + ball1.width/2;
p1.x1 = ball1.x + ball1.width/2 + ball1.hspeed;
p1.y = ball1.y + ball1.height/2;
p1.y1 = ball1.y + ball1.height/2 + ball1.vspeed;
var p2 = {};
p2.x = ball2.x + ball2.width/2;
p2.x1 = ball2.x + ball2.width/2 + ball2.hspeed;
p2.y = ball2.y + ball2.height/2;
p2.y1 = ball2.y + ball2.height/2 + ball2.vspeed;

if(Math.pow((p1.x-p2.x),2) + Math.pow((p1.y-p2.y),2) <= Math.pow((ball1.width/2 + ball2.width/2),2)
|| Math.pow((p1.x1-p2.x1),2) + Math.pow((p1.y1-p2.y1),2) <= Math.pow((ball1.width/2 + ball2.width/2),2)){
return true;
}
return false;
}

function addEvent(){
var addBtn = document.getElementById('add');
addBtn.onclick = function(){
var newdom = document.createElement('div');
newdom.setAttribute('id','ball_'+ seq);
newdom.setAttribute('class','ball');
document.getElementById('wrapper').appendChild(newdom);
var newBall = new Ball('ball_'+seq);
//小球出现的地方不能是和边界重叠,不能和已存在的小球重叠,这里还没有办法做
//小球出现的点随机
// newBall.dom.style.left = Math.floor(Math.random()*(boundary.width - 2*newBall.width) + newBall.width) + 'px';
// newBall.x = parseInt(newBall.dom.style.left);
// newBall.dom.style.top = Math.floor(Math.random()*(boundary.height - 2*newBall.height) + newBall.height) + 'px';
// newBall.y = parseInt(newBall.dom.style.top);

//小球出现的点固定
newBall.dom.style.left = 0 + 'px';
newBall.x = 0;
newBall.dom.style.top = newBall.dom.style.left;
newBall.y = newBall.x;

newBall.dom.style.visibility = 'visible';
newBall.dom.style.background = colors[(Math.floor(Math.random() * colors.length) + 1)];
balls.push(newBall);
seq++;
clearInterval(interval);//经过证明,一个页面可以有多个setInterval(定时器),这样省却了再interval里遍历数组的麻烦,但系统却要维护多个定时器,哪种办法更好些呢?
/*
以下内容来自网络,未亲测。
关于游戏中的定时器的设计有以下两种争议:

1) 每个需要定时器的地方都创建一个,然后问题归结为多个定时器的管理问题;

2) 游戏中只有一个定时器,然后问题归结为一个定时器实现多个定时器的效果。

实际从管理难度以及运行效率上来讲应该选择第2种。
*/
interval = setInterval(function(){
go(balls, boundary);
}, 20);
}
}

window.onload = function(){
init();
addEvent();
}
</script>
</head>
<body>
<div class="center">
<div id="wrapper">
<div id="ball" class="ball"></div>
</div>
<div style="text-align:left;"><button id="add">add</button></div>
</div>
</body>
</html>