js实现小球碰撞(数学之美)
<!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>