引言

在学习JavaScript的时候,看到一个练习事例,就想到能不能做成类似Windows屏幕保护气泡那种效果,经过不断思考尝试,最后做出的效果如下图:

思路

其实上面的那种效果就是模拟理想物理环境下的多个小球非对心碰撞(对心碰撞是其特殊情况),所谓理想物理情况就是没有外力作用的封闭系统,内部遵循动量守恒定律和能量守恒定律。假设小球A和小球B的质量分别为????,初始速度分别为????,碰撞后的速度分别为??'??',两个小球的碰撞瞬间的状态如下图:

其中??????是两小球沿球心连线方向上的分速度,??????是两小球垂直球心连线方向上的分速度。碰撞后,由于两小球在垂直球心连线方向上没有力的相互作用,所以速度不变,还是??????,沿球心连线方向上的分速度为???'???'。运用以下物理公式:

能量守恒定律:
??•??²/2+??•??²/2=??•??'²/2+??•??'²/2

向量运算:
??²= ???²+???²
??²= ???²+???²
??'²= ???'²+???²
??'²= ???'²+???²

推导得出:
??•???²/2+??•???²/2=??•???'²/2+??•???'²/2

再联合动量守恒定律:
??•???+??•???=??•???'+??•???'

推导得出:
???'=(???•(??-??)+2•??•???)/(??+??)
???'=(???•(??-??)+2•??•???)/(??+??)

最后合成碰撞后的速度??'??'就ok了!

代码实现

// 小球对象构造函数
function Ball(x, y, speedX, speedY, color, radius, density) {this.x = x;this.y = y;this.speedX = speedX;this.speedY = speedY;this.color = color;this.radius = radius;this.density = density;
}// 绘制
Ball.prototype.draw = function () {ctx.beginPath();ctx.fillStyle = this.color;ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);ctx.fill();
}// 边界碰撞检测
Ball.prototype.borderCollisionDetect = function () {if ((this.x + this.radius) >= width && this.speedX > 0) {this.speedX *= -1;}if ((this.x - this.radius) <= 0 && this.speedX < 0) {this.speedX *= -1;}if ((this.y + this.radius) >= height && this.speedY > 0) {this.speedY *= -1;}if ((this.y - this.radius) <= 0 && this.speedY < 0) {this.speedY *= -1;}
}
复制代码
// 创建小球对象
function createBalls() {require(['utils'], function (utils) {while (balls.length < 8) {var ball = new Ball(utils.random(0, width),  // xutils.random(0, height), // yutils.random(1, 8),     // speedXutils.random(1, 8),     // speedY'rgb('+utils.random(0, 255) +','+ utils.random(0, 255)+','+ utils.random(0, 255) +')',30,                     // radius1                       // density);balls.push(ball);}});
}
复制代码
// 更新小球速度和位置
function update() {for (let i = 0; i < balls.length; i++) {balls[i].borderCollisionDetect();for (let j = i + 1; j < balls.length; j++) {if (ballsCollisionDetect(balls[i], balls[j])) {collide(balls[i], balls[j]);}}// 更新位置balls[i].x += balls[i].speedX;balls[i].y += balls[i].speedY;}
}
复制代码
// 碰撞检测
function ballsCollisionDetect(ball1, ball2) {//  当前距离var dx = ball1.x - ball2.x;var dy = ball1.y - ball2.y;var distance = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));//  预测下一时刻会不会碰撞let dx_next = ball1.x + ball1.speedX - ball2.x - ball2.speedX;let dy_next = ball1.y + ball1.speedY - ball2.y - ball2.speedY;let distance_next = Math.sqrt(Math.pow(dx_next, 2) + Math.pow(dy_next, 2));if (distance_next < ball1.radius + ball2.radius && distance_next < distance) {return true;}return false;
}复制代码
// 更新碰撞后的状态
function collide(ball1, ball2) {require(['Vector2d'], function (Vector2d) {// 初始速度向量let speed_ball1_initial = new Vector2d(ball1.speedX, ball1.speedY);let speed_ball2_initial = new Vector2d(ball2.speedX, ball2.speedY);// 球心方向单位向量let s = new Vector2d(ball2.x - ball1.x, ball2.y - ball1.y);s = s.normalize();// 垂直球心方向单位向量let t = s.rotate(Math.PI / 2);// 速度在球心向量上的分速度投影let speed_ball1_initial_sc = speed_ball1_initial.dotProduct(s)/s.length();let speed_ball2_initial_sc = speed_ball2_initial.dotProduct(s)/s.length();// 速度在垂直球心向量上的分速度投影let speed_ball1_initial_tc = speed_ball1_initial.dotProduct(t)/t.length();let speed_ball2_initial_tc = speed_ball2_initial.dotProduct(t)/t.length();// 碰撞后球心方向上的分速度let speed_ball1_final_sc = (speed_ball1_initial_sc * (ball1.density * Math.pow(ball1.radius,3) - ball2.density * Math.pow(ball2.radius,3)) + 2 * (ball2.density * Math.pow(ball2.radius,3)) * speed_ball2_initial_sc)/ (ball1.density * Math.pow(ball1.radius,3) + ball2.density * Math.pow(ball2.radius,3));let speed_ball2_final_sc = (speed_ball2_initial_sc * (ball2.density * Math.pow(ball2.radius,3) - ball1.density * Math.pow(ball1.radius,3)) + 2 * (ball1.density * Math.pow(ball1.radius,3)) * speed_ball1_initial_sc)/ (ball1.density * Math.pow(ball1.radius,3) + ball2.density * Math.pow(ball2.radius,3));// 碰撞后球心方向上的分速度向量let speed_ball1_final_s = s.scale(speed_ball1_final_sc);let speed_ball2_final_s = s.scale(speed_ball2_final_sc);// 碰撞后垂直球心方向上的分速度向量let speed_ball1_final_t = t.scale(speed_ball1_initial_tc);let speed_ball2_final_t = t.scale(speed_ball2_initial_tc);// 结束速度向量let speed_ball1_final = speed_ball1_final_s.add(speed_ball1_final_t);let speed_ball2_final = speed_ball2_final_s.add(speed_ball2_final_t);// 更新速度ball1.speedX = speed_ball1_final.x;ball1.speedY = speed_ball1_final.y;ball2.speedX = speed_ball2_final.x;ball2.speedY = speed_ball2_final.y;});
}
复制代码

缺陷

本代码是通过window.requestAnimationFrame()方法循环执行来实现动画效果的,它的回调次数是每秒60次,所以对于一些速度"过快"的小球,会在撞击边界时出现"撞出去一小部分"的情况。还有本代码只考虑了两个小球相撞的情况,没有考虑三个以上小球同时相撞的场景。

结语

本代码是学习JavaScript时的实战演练,能加深对这门语言的理解和掌握。完整代码详见GitHub地址。

参考链接

www.lyblog.net/detail/397.…
www.cnblogs.com/kenkofox/ar…
tina0152.blog.163.com/blog/static…
www.51testing.com/html/66/n-8…

JavaScript之多小球非对心弹性碰撞相关推荐

  1. C++多小球非对心弹性碰撞(HGE引擎)

    程序是一个月前完成的,之前一直没正儿八经的来整理下这个程序,感觉比较简单,不过即使简单的东西也要跟大家分享下. 源码下载:http://download.csdn.net/detail/y851716 ...

  2. matlab非对心弹性斜碰,一种倾斜撞击的实验装置

    第 15卷 第4期 2017年 8月 实验科学与技术 Experiment Science and Technology V01.15 No.4 Aug.2017 一 种倾斜撞击的实验装置 李成伟 , ...

  3. 原生JavaScript实现五色小球

    原生JavaScript实现五色小球 一,HTML代码 <div id="ball"></div> <script src="undersc ...

  4. 你可能不知道系列--JavaScript严格模式与非严格模式的区别

    对于 JavaScript 严格模式与非严格模式的区别,强烈建议大家去查看权威文档 MDN: 严格模式,不要乱找百度,本文也主要是参考了 MDN 上对 JS 严格模式 的介绍. 首先,严格模式通过抛出 ...

  5. Windows Store App JavaScript 开发:小球运动示例

    通过前面内容的学习,相信读者已经对开发基于JavaScript的Windows应用商店应用有了一定的了解,本小节通过一个小球运动的示例来介绍如何新建一个JavaScript的Windows应用商店项目 ...

  6. JavaScript正则表达式/g和非/g的区别详解

    g是JavaScript正则表达式修饰符,在菜鸟教程中的解释为:执行全局匹配(查找所有匹配而非在找到第一个匹配后停止). 下面开始试验: var str = "up up";var ...

  7. html弹出非模式窗口,JavaScript模态窗口和非模态窗口(转)

    JavaScript中弹出的窗口有模态窗口和非模态窗口.模态窗口就是打开一个子窗口,如果这个子窗口不关闭,就不能操作它的父窗口,原来程序暂停执行,直到这个模态窗口关闭 后才回到原来程序继续.非模态的就 ...

  8. javascript间接实现前端非获取匹配,保留带某前缀的子串不执行替换

    <!DOCTYPE html> <html> <head> <script type="text/javascript">var s ...

  9. Javascript实现网页水印(非图片水印)

    1 概述 1.1 定义 在一些B/S结构的应用系统中,有很多页面是需要有水印的.常见的就是公文系统.合同系统等.大家常常关注的是网站图片增加水印,而很少关注页面水印.刚去Google了一圈,关于页面水 ...

  10. JavaScript严格模式与非严格模式区别

    开启严格模式方法"use strict";, 如果放在文件开头就是全局开启严格模式, 还可以在函数内声明, 这么做的话就是这个函数开启严格模式. 严格模式下无法再意外创建全局变量. ...

最新文章

  1. Gartner2014年魔力象限(商业智能和分析平台)
  2. 分布式事物-2pc和3pc区别
  3. Android 截图,截取指定view截图
  4. 文件传输-对数据进行加解密的方法!
  5. [翻译]用 Puppet 搭建易管理的服务器基础架构(3)
  6. java里赋值语句_java输入赋值语句
  7. 挑战程序设计竞赛-小笔记
  8. FPS游戏方框透视原理分析及实现-C++语言
  9. 在Windows里面使用binwalk工具分离图片
  10. python的数据类型分为复数_Python的基本数据类型
  11. python 图像拼接_python实现图像拼接功能
  12. 安利一波gif录制工具
  13. Illegal mix of collations (utf8mb4_unicode_ci,IMPLICIT) and (utf8mb4_general_ci,IMPLICIT) for operat
  14. Python技能树的测评和CSDN Markdown编辑器的测评
  15. tableau制作日历图学习
  16. MySQL的存储引擎、事务和锁机制
  17. KU040 slice内 路径分布 和 延时参数
  18. 关于MAX3232ESE+T的过热问题
  19. ti linux sdk 使用方法,关于AM335x 最新SDK ti-processor-sdk-linux-am335x-evm-06.03.00.106使用中遇到的问题...
  20. 【IOT专栏】当图像碰上区块链

热门文章

  1. 怎么恢复服务器上刚刚删除的文件,怎样恢复刚刚删除的文件 详细教程分享【图解】...
  2. 浅谈程序员常去的社区
  3. 自动化测试的流程是什么
  4. 关于java的http请求的工具类
  5. 一步一步教你写股票走势图——分时图三(对齐图表、自定义高亮)
  6. android 屏幕亮度代码,android 设置系统屏幕亮度
  7. 2015中国大学排行榜100强新鲜出炉(校友会版)-[转]
  8. qt 字体旋转90_如何识别图片和视频上文字的字体
  9. 【干货】如何紧跟未来的设计趋势:15 个让你永远不过时的资源
  10. 如何高效学习一门新技术