前面的话

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

碰撞检测

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

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

  由示意图可知,元素一的圆心位置为(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置L0if(L < L0){X = L0;}//如果脱离右侧范围,则left置为R0if(R > R0){X = R0 - EW;}//如果脱离上侧范围,则top置T0if(T < T0){Y = T0;}//如果脱离下侧范围,则top置为B0if(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函数系列第二篇——函数参数

    前面的话 javascript函数的参数与大多数其他语言的函数的参数有所不同.函数不介意传递进来多少个参数,也不在乎传进来的参数是什么数据类型,甚至可以不传参数.本文是深入理解javascript函数 ...

  2. JavaScript设计模式系列—模式篇总结(上)

    转载请注明预见才能遇见的博客:http://my.csdn.net/ 原文地址:https://blog.csdn.net/pcaxb/article/details/102517956 JavaSc ...

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

    前面的话 从本文开始,介绍javascript动画系列.javascript本身是具有原生拖放功能的,但是由于兼容性问题,以及功能实现的方式,用的不是很广泛.javascript动画广泛使用的还是模拟 ...

  4. javascript运动系列第二篇——变速运动

    前面的话 前面介绍过匀速运动的实现及注意事项,本文在匀速运动的基础上,更进一步,实现各种变速运动,包括加速运动.减速运动.缓冲运动.重力运动和弹性运动 准备工作 匀速运动 在原生javascript中 ...

  5. javascript动画系列第二篇——磁性吸附

    前面的话 上一篇,我们介绍了元素拖拽的实现.但在实际应用中,常常需要为拖拽的元素限定范围.而通过限定范围,再增加一些辅助的措施,就可以实现磁性吸附的效果 范围限定 如果我们限定元素只可以在可视范围内移 ...

  6. “睡服”面试官系列第九篇之数值的扩展(建议收藏学习)

    目录 1. 二进制和八进制表示法 2. Number.isFinite(), Number.isNaN() 3. Number.parseInt(), Number.parseFloat() 4. N ...

  7. ESP32系列--第九篇 ADC的使用

    一.目的 本篇主要介绍ESP32的ADC功能,ESP32有两个ADC模块,分别为ADC1/ADC2,每个ESP32系列具有的通道数不一样,详情请看下表. 在WiFi在使用时,ADC2的使用受到一些限制 ...

  8. javascript面向对象系列第一篇——构造函数和原型对象

    前面的话 一般地,javascript使用构造函数和原型对象来进行面向对象编程,它们的表现与其他面向对象编程语言中的类相似又不同.本文将详细介绍如何用构造函数和原型对象来创建对象 构造函数 构造函数是 ...

  9. LeetCode刷题复盘笔记—一文搞懂0 - 1背包之494. 目标和问题(动态规划系列第九篇)

    今日主要总结一下动态规划0-1背包的一道题目,494. 目标和问题 题目:494. 目标和 Leetcode题目地址 题目描述: 给你一个整数数组 nums 和一个整数 target . 向数组中的每 ...

最新文章

  1. SVO(SVO: fast semi-direct monocular visual odometry)
  2. 我这样写代码,比直接使用 MyBatis 效率提高了100倍!
  3. TensorFlow数据读取方式:Dataset API,以及如何查看dataset:DatasetV1Adapter的方法
  4. Hive 基本语法操练(四):Hive 复合类型
  5. 《JAVA练习题目1》 模拟彩票中奖
  6. 大数据量及海量数据处理算法总结
  7. AI也脸盲 |黑人遭人脸识别技术“误判”概率竟高出白人5至10倍!
  8. 第四章 Tomcat服务器的安装及配置2
  9. pt5 mysql预处理_技术分享 | MySQL 监控利器之 Pt-Stalk
  10. en55032最新标准下载_关于欧盟新版EMC标准EN55032的解析
  11. 模拟集成电路大牛的经验
  12. 关于时间序列分析的协整检验、脉冲响应图、方差分解图和格兰杰因果检验
  13. 【地理信息技术】 上机04 栅格数据的空间分析(一) 学校选址分析
  14. Linux 抓包工具 tcpdump
  15. 如何在微软的下载商店找到下载的图片?
  16. kernelbase故障模块_kernelbase.dll故障怎么解决
  17. Windows 10 低版本驱动数字签名更新的必要性
  18. 某计算机配置的内存为2GB,实测告诉你为APU分配64MB和2GB显存性能差距多大
  19. 使用MD中控件实现APP页面折叠
  20. 微信获取临时素材接口

热门文章

  1. 完整中英文世界国家级联下拉列表插件【前端版】
  2. 国内ntp时间服务器ip地址
  3. GDAL Data Model(转)
  4. 出色管理者的时间管理
  5. iOS环信聊天界面中点击头像和消息的几种状态
  6. 使用mvc框架搭建跟人站点
  7. 051 日志案例分析(PV,UV),以及动态分区
  8. Emacs 使用YASnippet
  9. oracle rac对心跳要求_关于心跳网络引起的Oracle RAC的节点驱逐(不是实例驱逐)...
  10. EOS共识机制——DPoS代理权益证明