js 小球碰壁反彈and小球碰撞
好像好幾天沒有更博了呢,最近有點變懶了,這樣不好,不好~~我們要做熱愛學習的好孩子,嘻嘻,今天下午補上。。。
我們在學習js的時候,一個很經典的案例就是小球的碰壁反彈效果啦~簡單的小球碰壁效果可以慢慢延伸,讓我們做出的效果更漂亮。如圖~
這個樣子放上好像有點單調,這個案例就是實現了多個小球的同時碰壁反彈,並且不同的小球碰撞後也可發生碰撞反應,相應的小球運動方向也會發生改變。
什麼事情都要從簡單開始,一點一點的去做,要想實現好多小球同時運動,首先就要搞清楚一個小球是怎麼去運動的。要想讓小球動起來,最常用的就是兩種方法,一種是要改變小球的margin值,使小球移動,還有一種就是利用position定位,讓小球動起來。我用了position定位的方法。我們可以利用計時器控制小球left和top值的變化,是小球一直移動。解決了小球移動的問題,第二個問題就是方向的變化,對小球的位置進行一個判斷,二維空間內,小球的運動可以分解為x方向和y方向上的運動,所以我們可以分別設定一個flag標記,對小球的位置進行判斷,等小球到達邊界時,就改變flag的值,使小球的運動方向發生改變。
實現了一個小球的運動,再去實現多個小球就容易啦,首先,我先建立了一個數組,陣列中的每一個元素都存一個物件,每個物件均包括一個x(小球的初始x值),y(小球的初始y值),cx(圓心x座標),cy(圓心y座標),movex(小球x軸運動方向),movey(小球y軸運動方向),bgcolor(小球的背景顏色),speed(小球運動速度),timer(小球運動計時器,每一個小球需要一個計時器控制這個小球的運動),index (小球的索引值)。
給每一個物件繫結一個計時器,這樣小球就可以動起來啦~
現在每一個小球都可以動啦,我們可以繼續優化,實現小球的碰撞反彈。博主的物理學的不大好,也比較懶,所以對於小球的反彈,就比較簡單粗暴的來寫了。首先先比較小球啊與除了本身之外的其他所有小球的圓心距,如果圓心距小於一個小球的直徑,即兩個小球便會發生碰撞事件。(方向的改變僅僅考慮了簡單碰撞後方向改變,並未考慮物理實際的碰撞反應,僅實現簡單的方向改變,速度不會改變)
如果小球b處於小球a的正上或正下,便只改變小球a的movey,
如果小球b處在小球a的正左或正右,便只改變小球a的movex,
如果小球a處在小球b的左下方,小球a向左下移動,
如果小球a處在小球b的左上方,小球a向左上移動,
如果小球a處在小球b的右下方,小球a向右下移動,
如果小球a處在小球b的右上方,小球a向右上移動,
html,css程式碼如下:
<style>
*{margin: 0;
padding:0;}
#main{margin: 0px auto;position: relative;}
#main div{overflow: hidden;position: absolute;width: 200px;height: 200px;opacity: 0.5;
-moz-border-radius: 50%;-webkit-border-radius: 50%;border-radius: 50%;}
</style>
<div id="main">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
所有的js程式碼:
<script>
var main = document.getElementById('main');
var circles = main.getElementsByTagName('div');
var st = [0,1,2,3,4,5,6,7,8,9,'a','b','c','d','e','f'];
var json = [],arr=[],color = [];
var maxW = 0;
var maxH = 0;
var cwidth = circles[0].offsetWidth;
var cheight = circles[0].offsetHeight;
//根據瀏覽器視窗的大小自動調節小球的運動空間
window.onresize=function(){
var main = document.getElementById('main');
maxW=window.innerWidth-circles[0].clientWidth;
maxH=window.innerHeight-circles[0].clientHeight;
main.style.width = window.innerWidth+'px';
main.style.height = window.innerHeight+'px';
}
onresize();
//陣列物件的初始化
for(var i=0;i<circles.length;i++){
arr=[];
for(var j=0;j<6;j++){
color[j] = st[Math.floor(Math.random()*16)];
}
arr.x = Math.floor(Math.random()*(maxW+1));//初始x座標
arr.y = Math.floor(Math.random()*(maxH+1));//初始y座標
arr.cx = arr.x + circles[0].offsetWidth/2;//圓心x座標
arr.cy = arr.y + circles[0].offsetHeight/2;//圓心y座標
arr.movex = Math.floor(Math.random()*2);//x軸移動方向
arr.movey = Math.floor(Math.random()*2);//y軸移動方向
arr.bgolor = '#'+ color.join('');//隨機生成一個6位字串
arr.speed = 2+Math.floor(Math.random()*5);
//隨機生成一個2~6之間的移動速度(如果設定的改變速度太大,容易造成小球碰撞時兩個小球之間有重合,也有可能小球會出界)
arr.timer = null;//計時器
arr.index = i;//索引值
json.push(arr);
circles[i].style.left = arr.x + 'px';//小球位置初始化
circles[i].style.top = arr.y + 'px';//小球位置初始化
circles[i].style.backgroundColor = arr.bgolor;//小球背景顏色初始化
}
//碰撞函式
function crash(a){
var ball1x = json[a].cx;
var ball1y = json[a].cy;
for(var i= 0;i<json.length;i++){
if(i!=a){
var ball2x = json[i].cx;
var ball2y = json[i].cy;
//圓心距離的平方
var len = (ball1x-ball2x)*(ball1x-ball2x)+(ball1y-ball2y)*(ball1y-ball2y);
if(len <= cwidth*cwidth){
//小球位置的判斷,發生碰撞反應
if(ball1x >ball2x){
if(ball1y > ball2y){
json[a].movex=1;
json[a].movey=1;
}else if(ball1y < ball2y){
json[a].movex=1;
json[a].movey=0;
}else{
json[a].movex=1;
}
}else if(ball1x < ball2x){
if(ball1y > ball2y){
json[a].movex=0;
json[a].movey=0;
}else if(ball1y < ball2y){
json[a].movex=0;
json[a].movey=1;
}else{
json[a].movex=0;
}
}else{
if(ball1y > ball2y){
json[a].movey=1;
}else if(ball1y < ball2y){
json[a].movey=0;
}
}
}
}
}
}
//移動函式
function move(circle){
circle.timer = setInterval(function () {
if(circle.movex == 1){
circle.x+=circle.speed;
if(circle.x+circle.speed >= maxW){//防止小球出界
circle.x = maxW;
circle.movex=0;//小球運動方向發生改變
}
}else{
circle.x-=circle.speed;
if(circle.x-circle.speed <= 0){
circle.x = 0;
circle.movex=1;
}
}
if(circle.movey == 1){
circle.y += circle.speed;
if(circle.y+circle.speed >= maxH){
circle.y = maxH;
circle.movey=0;
}
}else{
circle.y-=circle.speed;
if(circle.y-circle.speed <= 0){
circle.y = 0;
circle.movey=1;
}
}
circle.cx = circle.x + circles[0].offsetWidth/2;//小球每一次運動圓心都會發生改變
circle.cy = circle.y + circles[0].offsetHeight/2;
circles[circle.index].style.left = circle.x + 'px';//小球位置重定位
circles[circle.index].style.top = circle.y + 'px';
/*console.log('x'+circle.cx+'y'+circle.cy);*/
crash(circle.index);
},15);
}
//對每一個小球繫結計時器,讓小球動起來
for(var i=0;i<circles.length;i++){
move(json[i]);
}
</script>
其實在寫的過程中,也遇到了很多很多的bug,這已經是改進後的版本,也許現在的版本還有bug,期待大家來發現哦~