前面的话

  碰撞可以分为碰壁和互碰两种形式,上篇介绍了碰壁运动,本文将从浅入深地介绍碰撞运动的互碰形式

碰撞检测

  对于互碰形式的碰撞运动来说,首先要解决的是碰撞检测。对于矩形元素的碰撞检测前面的博文已经详细介绍过,下面主要介绍圆形元素的碰撞检测

  矩形元素的碰撞检测利用九宫格分析法,而圆形元素的碰撞检测则简单很多,判断两个圆形元素的半径之和是否大于两个圆形元素的圆心点坐标之间的距离即可

  由示意图可知,元素一的圆心位置为(x1,y1),半径为r1,元素二的圆心位置为(x2,y2),半径为r2

  两个元素圆心之间的距离len = Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2))

  当len<= r1+r2时,说明两个圆形元素发生碰撞

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="test1" style="height: 100px;width: 100px;background:pink;position:absolute;top:0;left:0;border-radius: 50%;"></div>
<div id="test2" style="height: 200px;width: 200px;background:orange;position:absolute;top:150px;left:150px;border-radius: 50%;"></div>
<script>
function getCSS(obj,style){
if(window.getComputedStyle){
return getComputedStyle(obj)[style];
}
return obj.currentStyle[style];
}
function bump(obj,objOther,bgColor){ /***被碰元素***/
var r1 = obj.offsetWidth/2;
var x1 = parseFloat(getCSS(obj,'left')) + r1;
var y1 = parseFloat(getCSS(obj,'top')) + r1;
/**侵入元素**/
var r2 = objOther.offsetWidth/2;
var x2 = parseFloat(getCSS(objOther,'left')) + r2;
var y2 = parseFloat(getCSS(objOther,'top')) + r2;
//碰撞检测
var len = Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)); if(len <= r1 + r2){
obj.style.backgroundColor = 'red';
}else{
obj.style.backgroundColor = bgColor;
}
} function drag(obj){
obj.onmousedown = function(e){
e = e || event;
//提升当前元素的层级
obj.style.zIndex = '1';
//获取元素距离定位父级的x轴及y轴距离
var x0 = this.offsetLeft;
var y0 = this.offsetTop;
//获取此时鼠标距离视口左上角的x轴及y轴距离
var x1 = e.clientX;
var y1 = e.clientY;
//鼠标按下时,获得此时的页面区域
var L0 = 0;
var R0 = document.documentElement.clientWidth;
var T0 = 0;
var B0 = document.documentElement.clientHeight;
//鼠标按下时,获得此时的元素宽高
var EH = obj.offsetHeight;
var EW = obj.offsetWidth;
document.onmousemove = function(e){
e = e || event;
//获取此时鼠标距离视口左上角的x轴及y轴距离
x2 = e.clientX;
y2 = e.clientY;
//计算此时元素应该距离视口左上角的x轴及y轴距离
var X = x0 + (x2 - x1);
var Y = y0 + (y2 - y1);
/******范围限定*******/
//获取鼠标移动时元素四边的瞬时值
var L = X;
var R = X + EW;
var T = Y;
var B = Y + EH;
//在将X和Y赋值给left和top之前,进行范围限定
//只有在范围内时,才进行相应的移动
//如果脱离左侧范围,则left置L0
if(L < L0){X = L0;}
//如果脱离右侧范围,则left置为R0
if(R > R0){X = R0 - EW;}
//如果脱离上侧范围,则top置T0
if(T < T0){Y = T0;}
//如果脱离下侧范围,则top置为B0
if(B > B0){Y = B0 - EH;}
obj.style.left = X + 'px';
obj.style.top = Y + 'px';
//运行碰撞检测函数
bump(test2,test1,'orange')
}
document.onmouseup = function(e){
//降低当前元素的层级
obj.style.zIndex = '0';
//当鼠标抬起时,拖拽结束,则将onmousemove赋值为null即可
document.onmousemove = null;
//释放全局捕获
if(obj.releaseCapture){
obj.releaseCapture();
}
}
//阻止默认行为
return false;
//IE8-浏览器阻止默认行为
if(obj.setCapture){
obj.setCapture();
}
}
}
drag(test1);
drag(test2);
</script>
</body>
</html>

无损碰撞

  假设两个元素的碰撞,对元素的速度并不产生损耗,而只是改变元素速度方向

  假设元素一在与元素二碰撞前的瞬时速度是v,将该速度分解为平行于碰撞方向的速度v1和垂直于碰撞方向的速度v2

  碰撞发生后,碰撞方向的速度v1变成了反向的v1

  将反向的v1分解到水平方向v1x和垂直方向v1y

  将垂直于碰撞方向的速度v2分解到水平方向v2x和垂直方向v2y

    水平方向的速度vx = v2x - v1x
垂直方向的速度vy = v2y - v1y

  元素二的速度分解形式与元素一类似,就不再赘述

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<button id="btn1">开始运动</button>
<button id="reset">还原</button>
<div id="test1" style="height: 150px;width: 150px;background:pink;position:absolute;top:50px;left:50px;border-radius: 50%;"></div>
<div id="test2" style="height: 150px;width: 150px;background:orange;position:absolute;top:250px;left:250px;border-radius: 50%;"></div>
<script>
//声明元素的步长值
//步长值默认值为[-25,-20,-15,-10,-5,0,5,10,15,20]中的一个随机数
test1.stepX = 5*Math.floor(Math.random() * 10 - 5);
test1.stepY = 5*Math.floor(Math.random() * 10 - 5);
test2.stepX = 5*Math.floor(Math.random() * 10 - 5);
test2.stepY = 5*Math.floor(Math.random() * 10 - 5);
btn1.onclick = function(){
collisionMove({
obj:test1
})
collisionMove({
obj:test2
})
}
reset.onclick = function(){
history.go();
}
function collisionMove(json){
var obj = json.obj;
var fn = json.fn;
//声明x、y轴的当前值
var curX,curY;
//声明x、y轴方向
var dirX = json.dirX;
var dirY = json.dirY;
dirX = obj.stepX > 0 ? '+' : '-';
dirY = obj.stepY > 0 ? '+' : '-';
//声明offset宽高
var offsetWidth = obj.offsetWidth;
var offsetHeight = obj.offsetHeight;
//声明元素活动区域宽高
var activeWidth = json.activeWidth;
var activeHeight = json.activeHeight;
//元素获取区域宽高默认值为可视区域宽高
activeWidth = Number(activeWidth) || document.documentElement.clientWidth;
activeHeight = Number(activeHeight) || document.documentElement.clientHeight;
//声明left、top样式值
var left,top;
//清除定时器
if(obj.timer){return;}
//开启定时器
obj.timer = setInterval(function(){
//获取x、y轴的当前值
curX = parseFloat(getCSS(obj,'left'));
curY = parseFloat(getCSS(obj,'top'));
bump(test1,test2);
//更新left、top值
left = curX + obj.stepX;
top = curY + obj.stepY;
//右侧碰壁前一刻,步长大于剩余距离,且元素向右运动时
if((left > activeWidth - offsetWidth) && (dirX == '+')){
left = activeWidth - offsetWidth;
}
//左侧碰壁前一刻,步长大于剩余距离,且元素向左运动时
if((Math.abs(obj.stepX) > curX) && (dirX == '-')){
left = curX;
}
//下侧碰壁前一刻,步长大于剩余距离,且元素向下运动时
if((top > activeHeight - offsetHeight) && (dirY == '+')){
top = activeHeight - offsetHeight;
}
//上侧碰壁前一刻,步长大于剩余距离,且元素向上运动时
if((Math.abs(obj.stepY) > curY) && (dirY == '-')){
top = curY;
}
obj.style.left= left + 'px';
obj.style.top = top + 'px';
//左侧或右侧碰撞瞬间
if(left == activeWidth - offsetWidth || left == curX){
obj.stepX = -obj.stepX;
}
//上侧或下侧碰撞瞬间
if(top == activeHeight - offsetHeight || top == curY){
obj.stepY = -obj.stepY;
}
//更新运动方向
dirX = obj.stepX > 0 ? '+' : '-';
dirY = obj.stepY > 0 ? '+' : '-';
},20);
} function getCSS(obj,style){
if(window.getComputedStyle){
return getComputedStyle(obj)[style];
}
return obj.currentStyle[style];
}
//碰撞检测函数
function bump(obj,objOther){
/***动态元素***/
obj.r = obj.offsetWidth/2;
obj.x0 = parseFloat(getCSS(obj,'left')) + obj.r;
obj.y0 = parseFloat(getCSS(obj,'top')) + obj.r;
/**静态元素**/
objOther.r = objOther.offsetWidth/2;
objOther.x0 = parseFloat(getCSS(objOther,'left')) + objOther.r;
objOther.y0 = parseFloat(getCSS(objOther,'top')) + objOther.r;
//圆心之间的距离
var len = Math.sqrt((obj.x0-objOther.x0)*(obj.x0-objOther.x0) + (obj.y0-objOther.y0)*(obj.y0-objOther.y0));
//发生碰撞
if(len <= obj.r + objOther.r){
//碰撞方向与水平负方向的夹角a
var a = Math.atan(Math.abs((obj.y0-objOther.y0)/(obj.x0-objOther.x0)));
stepChange(test1,test2,a);
stepChange(test2,test1,a);
}
}
//碰撞时,步长变化函数
function stepChange(obj,objOther,a){
//步长合并
obj.step = Math.sqrt(obj.stepX*obj.stepX + obj.stepY*obj.stepY);
//假设总步长方向与x轴方向的夹角为b
obj.b = Math.atan(Math.abs(obj.stepY/obj.stepX));
//假设总步长方向与碰撞方向的夹角为c
obj.c = Math.abs(a - obj.b);
//步长分解
//碰撞方向
obj.step1 = obj.step*Math.cos(obj.c);
//垂直方向
obj.step2 = obj.step*Math.sin(obj.c);
//按照运动元素(侵入元素)的起始运动方向对步长进行重新分解
//左上
if(obj.x0 <= objOther.x0 && obj.y0 <= objOther.y0){
obj.stepX = -obj.step1*Math.cos(a) + obj.step2*Math.sin(a)
obj.stepY = -obj.step1*Math.sin(a) - obj.step2*Math.cos(a)
}
//左下
if(obj.x0 < objOther.x0 && obj.y0 > objOther.y0){
obj.stepX = -obj.step1*Math.cos(a) + obj.step2*Math.sin(a)
obj.stepY = obj.step1*Math.sin(a) + obj.step2*Math.cos(a)
}
//右上
if(obj.x0 > objOther.x0 && obj.y0 < objOther.y0){
obj.stepX = obj.step1*Math.cos(a) - obj.step2*Math.sin(a)
obj.stepY = -obj.step1*Math.sin(a) - obj.step2*Math.cos(a)
}
//右下
if(obj.x0 > objOther.x0 && obj.y0 > objOther.y0){
obj.stepX = obj.step1*Math.cos(a) - obj.step2*Math.sin(a)
obj.stepY = obj.step1*Math.sin(a) + obj.step2*Math.cos(a)
}
}
</script>
</body>
</html>

有损碰撞

  匀速有损碰撞是在无损碰撞的基础上,每次碰撞都有一定的速度损耗,在碰撞或碰壁的瞬间乘以损耗因子即可

  变速有损碰撞类似于击打台球,元素运动时,速度就一直在减小,碰撞或碰壁时,除了速度方向改变外,速度也有所损耗,当速度减小到0时,停止运动。由于代码相似,就不再重复,源码见此

javascript运动系列第九篇——碰撞运动的更多相关文章

  1. 深入理解javascript函数系列第一篇——函数概述

    × 目录 [1]定义 [2]返回值 [3]调用 前面的话 函数对任何一门语言来说都是一个核心的概念.通过函数可以封装任意多条语句,而且可以在任何地方.任何时候调用执行.在javascript里,函数即 ...

  2. 深入理解javascript函数系列第二篇——函数参数

    × 目录 [1]arguments [2]内部属性 [3]函数重载[4]参数传递 前面的话 javascript函数的参数与大多数其他语言的函数的参数有所不同.函数不介意传递进来多少个参数,也不在乎传 ...

  3. 深入理解javascript作用域系列第二篇——词法作用域和动态作用域

    × 目录 [1]词法 [2]动态 前面的话 大多数时候,我们对作用域产生混乱的主要原因是分不清楚应该按照函数位置的嵌套顺序,还是按照函数的调用顺序进行变量查找.再加上this机制的干扰,使得变量查找极 ...

  4. 深入理解javascript作用域系列第二篇

    前面的话 大多数时候,我们对作用域产生混乱的主要原因是分不清楚应该按照函数位置的嵌套顺序,还是按照函数的调用顺序进行变量查找.再加上this机制的干扰,使得变量查找极易出错.这实际上是由两种作用域工作 ...

  5. 深入理解javascript函数系列第一篇

    前面的话 函数对任何一门语言来说都是核心的概念.通过函数可以封装任意多条语句,而且可以在任何地方.任何时候调用执行.在javascript里,函数即对象,程序可以随意操控它们.函数可以嵌套在其他函数中 ...

  6. javascript运动系列第一篇——匀速运动

    × 目录 [1]简单运动 [2]定时器管理 [3]分享到效果[4]移入移出[5]运动函数[6]透明度[7]多值[8]多物体[9]回调[10]函数完善[11]最终函数 前面的话 除了拖拽以外,运动也是j ...

  7. javascript动画系列第一篇——模拟拖拽

    × 目录 [1]原理介绍 [2]代码实现 [3]代码优化[4]拖拽冲突[5]IE兼容 前面的话 从本文开始,介绍javascript动画系列.javascript本身是具有原生拖放功能的,但是由于兼容 ...

  8. 深入理解javascript作用域系列第一篇——内部原理

    × 目录 [1]编译 [2]执行 [3]查询[4]嵌套[5]异常[6]原理 前面的话 javascript拥有一套设计良好的规则来存储变量,并且之后可以方便地找到这些变量,这套规则被称为作用域.作用域 ...

  9. 深入理解javascript作用域系列第一篇

    前面的话 javascript拥有一套设计良好的规则来存储变量,并且之后可以方便地找到这些变量,这套规则被称为作用域.作用域貌似简单,实则复杂,由于作用域与this机制非常容易混淆,使得理解作用域的原 ...

随机推荐

  1. what do i get for?

    有时候经常反思,做事究竟为了什么? 有时候,又无法解释,过去做的一些事情,是为了什么? 就像上小学时候明明挺到讨厌学习的,但上了初中后就开始玩命学习了 上高中时候认为自己真的是喜欢上学习了,上大学尼玛 ...

  2. 解决jquery新加入的元素没有绑定事件问题

    在使用jquery操作时,往往需要动态的添加一些元素,但是这些新加入的元素并没有像css那样被赋予原本定义的样式,解决方法如下: 1.动态插入元素后,并用bind事件给新加入的元素绑定事件. 2.如果 ...

  3. 【ORACLE】MD5加密

        今天乌干达充值卡入库时,发现有资源已经存在的异常, 异常原因经过核实是由于卡资源密码在库中已经存在, 为进一步查找存在的原因, 因此需要对导入文件密码的MD5 加密, 通过MD5加密后的字符串 ...

  4. 一张图系列——为什么在DllMain里面创建了线程并Wait会卡死

    这是一个老话题了,推荐一篇文章: http://blog.csdn.net/breaksoftware/article/details/8150476#0-tsina-1-83826-39723281 ...

  5. HTTP协议入门要点

    应用层协议.基于tcp HTTP/0.9 命令 GET 特点 服务器只能回应HTML字符串 服务器发送完毕后就关闭tcp连接 HTTP/1.0 命令 GET POST HEAD 特点 每次通信都必须包 ...

  6. sublime_text_2 ubuntu下无法输入中文 解决方法

    参考资料:http://my.oschina.net/wugaoxing/blog/121281 环境配置:ubuntu 14.10 1.保存下述代码为 sublime_imfix.c 文件 /* s ...

  7. 李洪强iOS经典面试题155 - const,static,extern详解(面试必备)

    李洪强iOS经典面试题155 - const,static,extern详解(面试必备) 一.const与宏的区别(面试题): const简介:之前常用的字符串常量,一般是抽成宏,但是苹果不推荐我们抽 ...

  8. ThreadPoolExecutor源码学习(1)-- 主要思路

    ThreadPoolExecutor是JDK自带的并发包对于线程池的实现,从JDK1.5开始,直至我所阅读的1.6与1.7的并发包代码,从代码注释上看,均出自Doug Lea之手,从代码上看JDK1. ...

  9. xml中DTD解析

    DTD的作用是"文档类型的定义" DTD申明始终以<!DOCTYPE开头(开头后空一格). 本标签一共有三种写法 一.内部DTD: <!DOCTYPE 根元素 [ 文档 ...

  10. DataTable转List(备忘)

    public static List ToList(DataTable dt) { List<Dictionary<string, object>> list = new Li ...