JavaScript小球碰撞仿真程序开发教程
本文通过一个完整的小球碰撞仿真案例,详细讲解JavaScript在Web动画中的应用。内容涵盖HTML资源加载、DOM对象定位、setTimeout循环控制以及基于向量运算的碰撞处理逻辑。教程深入解析了如何避免浮点精度误差导致的显示异常,并提供了墙壁碰撞与球间碰撞的具体代码实现思路,适合希望深入理解JS动画原理的开发者参考。
JavaScript小球碰撞仿真程序开发教程
JavaScript的发展历史虽然不长,但演进速度极快。早期由NetScape开发的LiveScript语言,为Navigator浏览器及Web服务器提供了基础的脚本编写能力。随着Navigator 2.0版本引入Java小程序功能,NetScape将LiveScript更名为JavaScript,这标志着JavaScript的正式诞生。
本文将以一个趣味小球碰撞仿真程序为切入点,介绍JavaScript的基本语法与功能。该程序能够模拟弹性碰撞以及动能有损的不完全恢复碰撞。若要深入理解球间碰撞的处理逻辑,需要掌握矢量坐标旋转及基础物理学知识。即便对这些概念不熟悉也无妨,因为碰撞处理仅涉及几个函数调用,不影响对JavaScript核心知识的学习。编写此类带有技术趣味的示例,旨在激发读者对JavaScript的兴趣。
不再赘述,直接进入正题。
第一步:准备素材
第一步:准备素材
需要制作两张小球图片,分别为红球和蓝球,各一张。图片格式需为GIF。建议先使用3D软件制作立体球体效果,再转换为GIF格式,以增强动画的真实感。需注意图片背景必须透明,否则会影响视觉效果。此外,还需准备一张背景图,格式同样为GIF。
第二步:构建主页骨架
第二步:构建主页骨架
1. 在NetObject ScriptBuilder中使用New命令生成HTML文件骨架。在新生成的HTML文件中,将TITLE标签内的文字修改为“小球碰撞仿真演示程序”。
2. 添加背景图案和小球图片资源。
将BODY标签修改为指定背景图,其中background属性指定window.gif作为背景图案。
为了加载小球图片资源,在HTML标签之间添加以下两句代码:
这两句HTML脚本含义相同,IMG标签用于插入图片资源,SRC属性指定图片文件名。ID属性定义图片的对象名称,这是关键属性,后续JavaScript编程将依赖它。例如第一句中,BallBlue被指定为图片资源的对象名(可视为变量名)。STYLE属性定义图片在浏览器中的样式,其中“position”设为“absolute”,启用绝对坐标模式定位图片,而非文本流模式。
HTML部分编写完毕,接下来使用JavaScript使图片动起来。
第三步:编写JavaScript程序
第三步:编写JavaScript程序
1. 仿真程序利用窗口对象的SetTimeout()方法循环调用Move_Step()函数,以实现图片移动。Move_Step()函数根据速度矢量计算移动步长和方向。碰撞处理分为两部分:球对墙壁的碰撞处理,由Ball_Crash_Wall()函数负责;球体之间的碰撞处理,由Ball_Crash_Ball()函数负责。还需设置一些碰撞标志变量以记录当前碰撞状态,避免仿真误差导致的错误。尽管目前可能不理解误差为何导致错误,后续会详细解释。
2. 图片对象通过style.pixelLeft和style.pixelTop属性指定在窗口上的位置。
这两个属性是图片运动的关键。但在计算移动位置时,并不直接使用它们,而是使用数组变量BallCoord记录图片当前坐标。因为style.pixelLeft和style.pixelTop是整型变量,而碰撞计算会产生小数。若直接使用这两个属性记录位置,不仅存在误差,更严重的是在窗口边缘发生某些类型的球间碰撞时,可能导致小球被撞出屏幕的错误。数组变量支持浮点型,用于存放小数,从而避免此类错误。
变量定义与程序初始化
(一)变量定义与程序初始化
JavaScript语句可放置在HTML文件的主体(BODY区)或文件头(HEAD区)。区别在于,HEAD区通常存放预制好的函数和全局变量,供BODY区调用;而BODY区通常包含程序初始语句。JavaScript变量定义较为自由,但仍需遵循先定义后使用的基本规则。
1. 在HEAD区中定义全局变量:
Window_Top = 22 //窗口顶端位置。
Window_Left = 22 //窗口左端位置。
Window_Bottom = 294 //窗口底端位置。
Window_Right = 590 //窗口右端位置。
Setp_Proportion = 3.00 //速度放大比率。
BallCoord = new Array(new Array(2),new Array(2)) //定义二维数组,记录两球当前坐标。
BallDiametre = new Array(66,92) //定义一维数组,存放两球直径,值分别为66和92。
BallMoveVector = new Array(new Array(2,1),new Array(1,-2))//定义二维数组,记录两球速度向量,第二维分别存放X与Y轴速度。
CrashingWall = new Array(new Array(false,false,false,false),new Array(false,false,false,false))//数组CrashingWall用于避免墙壁碰撞的二次计算,值为true表示当前处于碰撞状态,初值为false。
2. 在BODY区中完成程序初始化工作,内容如下:
此处已展示JavaScript程序的基本框架。SCRIPT标签告知浏览器后续内容为脚本,LANGUAGE="JavaScript"指明语言为JavaScript。无论位于BODY区还是HEAD区,JavaScript代码必须包含在SCRIPT标签之间。程序中通过new Array()定义数组Balls,用于存放图片对象。随后语句Balls[0] = BallBlue将图片对象BallBlue赋给Balls[0],注意BallBlue正是加载图片时指定的对象名。语句Balls[0].style.pixelTop = BallCoord[0][0] = 100,同时将值100赋给数组变量BallCoord[0][0]和图片对象的style.pixelTop属性。这使得坐标数组变量BallCoord[0][0]获得初值,并指定了球1的初始顶端位置。其余语句含义类似。最后一句调用函数Move_Step()使小球移动一步。
单步移动函数Move_Step()讲解
(二)单步移动函数Move_Step()讲解
在HTML文件的HEAD区中编写Move_Step()函数,内容如下:
function Move_Step() {
Ball_Crash_Ball() //调用球间碰撞处理函数
Balls_Crash_Wall(0) //调用蓝球碰墙处理函数
Balls_Crash_Wall(1) //调用红球碰墙处理函数
//计算移动步长
BallCoord[0][0] += BallMoveVector[0][0] * Setp_Proportion
BallCoord[0][1] += BallMoveVector[0][1] * Setp_Proportion
BallCoord[1][0] += BallMoveVector[1][0] * Setp_Proportion
BallCoord[1][1] += BallMoveVector[1][1] * Setp_Proportion
//移动一步
Balls[0].style.pixelTop = BallCoord[0][0]
Balls[0].style.pixelLeft = BallCoord[0][1]
Balls[1].style.pixelTop = BallCoord[1][0]
Balls[1].style.pixelLeft = BallCoord[1][1]
window.setTimeout("Move_Step()",100) //100毫秒后再次调用Move_Step()函数
}
该函数的大部分含义可通过注释理解,此处仅对几点进行说明。
1. 在语句BallCoord[0][0] += BallMoveVector[0][0] * Setp_Proportion中,BallMoveVector数组存放速度向量。Setp_Proportion变量存放步长放大比率,BallMoveVector[0][0]乘以Setp_Proportion得到移动一步的长度。最后将该值加上BallCoord[0][0]原值后赋给BallCoord[0][0]本身,得到移动后的位置。
2. 语句window.setTimeout("Move_Step()",100)表示100毫秒后再次调用Move_Step()函数。利用setTimeout()方法,程序得以不断循环运行。
Ball_Crash_Ball()是小球之间的碰撞处理函数,只需调用即可。若不使用该函数,程序仍可运行,但缺少球间碰撞处理功能。(在Ball_Crash_Ball()函数中调用了CrashEnd_Speed()与atan360(x,y)函数,并使用了全局变量CrashingBalls,因此要正确使用它,必须将上述函数与变量拷贝至程序中)。
墙壁碰撞处理函数Balls_Crash_Wall()
(三)在HEAD区中编写墙壁碰撞处理函数Balls_Crash_Wall()
一个简化的函数如下:
function Balls_Crash_Wall(i) {
if( BallCoord[i][0] <= Window_Top)
BallMoveVector[i][0] = - BallMoveVector[i][0]
if ( BallCoord[i][1] <= Window_Left)
BallMoveVector[i][1] = - BallMoveVector[i][1]
if ( BallCoord[i][0] + BallDiametre[i] >= Window_Bottom)
BallMoveVector[i][0] = - BallMoveVector[i][0]
if ( BallCoord[i][1] + BallDiametre[i] >= Window_Right)
BallMoveVector[i][1] = - BallMoveVector[i][1]
}
该函数首先判断球是否已碰到墙壁,若是则将相应方向上的速度反向,大小不变。但该函数可能存在错误(原因较为复杂,此处仅作提示。由于小球移动步长往往大于1个点,且仿真为离散进行,某些情况下会发生错误)。需使用数组变量CrashingWall来标识碰撞状态,使得小球与墙壁处于碰撞状态时不再发生第二次碰撞。相应语句需修改,例如第一句改为:
if( BallCoord[i][0] <= Window_Top)
{ if (!CrashingWall[i][0]) { BallMoveVector[i][0] = - BallMoveVector[i][0] }
CrashingWall[i][0] = true
} else CrashingWall[i][0] = false
语句中CrashingWall数组类型为逻辑型,记录球与墙壁是否正在发生碰撞。当处于碰撞状态时,无需再将球的运动方向反向。
至此程序编写基本完成,其他详细内容见光盘中的原程序及说明。现在在NetObject ScriptBuilder中选择菜单Preview项,运行程序。
附注:
1. 运行本例程需先安装IE4.0以上浏览器或Netscape Navigator 4.0以上浏览器
2. 球间碰撞处理提示:当两球发生碰撞时,计算两球圆心连线与X轴的夹角。依据此夹角对坐标进行旋转,将原速度矢量映射到旋转后的坐标中,从而将球间的任意碰撞转变为正对心碰撞。利用冲量定理可推出碰后的速度计算公式,再将坐标旋转回原始坐标,即可得到碰撞后的速度矢量。上述文字看似复杂,其实程序中仅用11行语句便完成了全部计算。
JS监听滚动事件:回到顶部按钮的显示与隐藏逻辑
2026-11-05 16:19:37
JavaScript怎么读写LocalStorage与SessionStorage
2026-11-05 15:40:11
anime.js 与 GSAP 对比:轻量级项目该怎么选择动画库
2026-11-05 11:24:55
flv.js 与 hls.js 在直播场景下的选型对比
2026-11-05 08:18:52
JavaScript能做什么?前端开发中的作用与应用场景解析
2026-11-04 23:17:54
