找回密码
 立即注册
首页 业界区 业界 制作一个炫酷的多小球碰碰的 JS 网页特效,入门弹性碰撞 ...

制作一个炫酷的多小球碰碰的 JS 网页特效,入门弹性碰撞模拟和类的应用

皇甫佳文 2025-6-3 00:32:09
目录

  • 前言
  • 先画一个圆
  • 完善我们的类
  • 小球动起来
  • 最简单的碰撞计算,接触墙壁反弹
  • 向量类的完善
  • 检测两小球之间的碰撞
  • 完善碰撞的效果
  • 重复计算的问题
  • 撞击墙壁定格问题
  • 内存问题
  • 随机数生成多个小球
  • 参考资料

前言

在前端开发里,canvas 是 HTML5 里最炫酷的工具。我们今天就来搞一个这样的梦幻的效果,学习一下 ES6 的类在开发一个完整项目的思路(即 ES5 的构造函数),还有物理碰撞的程序的实现,当然,效果也很酷炫!
完整代码在此处。
先画一个圆

使用“类”这种被广泛应用的面向对象的概念,我们可以更好的整理我们的代码,做出更大的项目。
所以我们先创建一个  画板的类 class Canvas { } ,以便抽象我们之后对  的操作。
然后再向类里添加第一个方法 drawCircle() ,作为我们的测试吧,就是先画一个最简单的元素 --- 圆!
完整代码如下 (可以在 这个编辑器 进行简单调试):
  1. [/code]在代码里,我们定义了一个圆的属性,即 位置 x y 和半径 、 颜色。通过这种井井有条又优雅的方式,我们的目的就达到了!
  2. [align=center] 1.png [/align]
  3. 这就是一切的基础,一切从这里开始。
  4. [size=5]完善我们的类[/size]
  5. 我们直接使用 ball 显然是不够的,小球它们要有自己的思想,我们的 Canvas 类要只负责绘制,所以我们需要重新开辟一个类,叫 Ball 类,来处理它们自己的“思想”。
  6. 而 canvas 类也需要更多的可扩展性,今天我们是画圆,明天我们想画圈、方块,我们也要考虑到,所以现在,我们要完善一下。
  7. 完整代码如下,这样就完美了 ~
  8. [code]
复制代码
图像能画出来,那么下一步就是运动了。这个要复杂了,一下子想不到要怎么弄,所以要一步一步来。
小球动起来

我们想一下,小球动起来,必定需要把画板清空,然后更改位置、绘制,再清空,再更改位置、绘制... 一帧一帧来。
所以,

  • 画板需要有一个方法,清空画板 方法
  • 计算小球下一帧的位置
  • 再封装一个 【一键更新数据】,用于操作更新数据的逻辑,以及记录和返回计算的结果(表示当前一帧整个游戏的宏观状态)
(第三点的这种思想,可以看这个文章)
先实现第一条,这个很好搞,canvas 只需要使用白色画笔,画一个覆盖全画板的矩形即可:
(不过,我们可以不使用纯白,使用 0.4  的透明度,可以一点一点将上一帧给缓缓刷白,效果很好!)
  1. clearDisplay(){  // 清空画布
  2.         this.ctx.fillStyle = 'rgba(255, 255, 255, 0.4)';  // 这个透明度 0.4 是精华,绘制轨迹效果的关键
  3.         this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
  4. }
复制代码
然后是第二条。
小球如果要运动,必然需要知道要往哪里运动。现在我们引入物理的概念 --- 速度(velocity),这是一个向量值。
而下一帧要去的地方,就是当前的位置,加上当前的速度向量。比如速度是向右 5m/s,那下一秒的位置就是当前位置加上向右 5 米。
这是属于球的个人的“思想”,所以我们写到 Ball 类里面,同时 球 也要加上 速度 这个属性,位置和速度都是向量,都是 x y。
(当然,向量又是一个复杂的个体,所以我们需要再单独开辟一个向量类 Vector )
  1. // 球类
  2. class Ball {
  3.         constructor(config){
  4.                 Object.assign(this,{
  5.                         type : 'circle',
  6.                         position : new Vector(100, 100),  // 位置也是向量
  7.                         velocity : new Vector(5, 3),  // 当前的速度
  8.                         color : 'blue',
  9.                         radius : 25,
  10.                 },config);
  11.         }
  12.         nextFrameUpdate(){  // 计算下一帧,小球的位置
  13.                 return new Ball({
  14.                         ...this,  // 其他属性保持不变
  15.                         position: this.position.add(this.velocity),  // 所谓的计算,其实就是根据向量 +1
  16.                 });
  17.         }
  18. }
复制代码
在 canvas 里,x 和  y  的两个正方向如图所示,所以当前小球的速度是向右下:
2.png

下面就是我们当前的向量类 Vector :
  1. // 向量(可作为位置 和 速度)
  2. class Vector {
  3.         constructor(x, y) {
  4.                 this.x = x;
  5.                 this.y = y;
  6.         }
  7.         add(vector) {  // 两个向量相加,就是这样
  8.                 return new Vector(this.x + vector.x, this.y + vector.y);
  9.         }
  10. }
复制代码
然后,就是使用 js 里用烂了的 requestAnimationFrame 让这个画面一帧一帧动起来,它是根据浏览器的性能实时智能控制帧率的,一般是 100帧/s  左右。不熟悉的同学可以看这个 MDN 的介绍 。
3.gif

完整的代码如下:
  1. [/code][size=5]最简单的碰撞计算,接触墙壁反弹[/size]
  2. 这个,还几乎用不到物理碰撞算法之类。其实实现这个功能特别简单,只需要检测到小球到达墙壁边界,然后相应的速度正负转化一下即可!
  3. 代码很简单,很易懂,将 Ball 类里的 nextFrameUpdate 计算下一帧位置 的这个方法添加两个判断即可:
  4. [code]nextFrameUpdate(displayState){  // 计算下一帧,小球的位置
  5.         // 如果小球左右到达边界,X 速度取反
  6.         if (this.position.x >= displayState.displayEle.canvas.width - this.radius || this.position.x <= this.radius) {
  7.                 this.velocity = new Vector(-this.velocity.x, this.velocity.y);
  8.         }
  9.         // 如果小球上下到达边界,Y 速度取反
  10.         if (this.position.y >= displayState.displayEle.canvas.height - this.radius || this.position.y <= this.radius) {
  11.                 this.velocity = new Vector(this.velocity.x, -this.velocity.y);
  12.         }
  13.         return new Ball({
  14.                 ...this,  // 其他属性保持不变
  15.                 position: this.position.add(this.velocity),
  16.         });
  17. }
复制代码
每当 collisions 元素的数量达到 10 个以上,就只保留最后三个元素。
这样,我们就基本完成碰撞的检测和碰撞的效果了 ~ 我们来实验一下效果吧!
完整代码:
  1. [/code][size=5]随机数生成多个小球[/size]
  2. 现在,我们就可以写一个循环和随机数结合的脚本,生成一大堆个小球,像开头的那个动画一样的效果了。
  3. [code]dotProduct(vector) {  // 数量积
  4.         return this.x * vector.x + this.y * vector.y;
  5. }
复制代码
最后的效果如下面这个页内框架所示:
参考资料


  • https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial
  • https://gist.github.com/joshuabradley012/bd2bc96bbe1909ca8555a792d6a36e04
  • https://en.wikipedia.org/wiki/Elastic_collision#Two-dimensional
  • https://eloquentjavascript.net/16_game.html

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册