如何用JS实现炫酷小球弹性碰撞效果?

2026-05-21 02:234阅读0评论SEO优化
  • 内容介绍
  • 文章标签
  • 相关推荐

哎呀, 来点 JS 的小球弹性碰撞吧,先别急着看代码,先给你打个底:这个效果一做就会让你心跳加速,仿佛在浏览器里玩的是弹珠台。

说起小球碰撞,我最想先告诉你一句话:如果你不想玩得像个程序猿,就别去学物理。主要原因是要搞到真正的弹性碰撞, 你得先把两颗球的速度、 别怕... 质量、角度全部算清楚,然后再让它们在画布上“咔嚓”一声反弹回来。这可不是随便跑个循环就能搞定的事儿。

JS 实现弹性碰撞物理效果,并搞了一个炫酷的小球碰撞特效

先准备一个画布——Canvas

我们用 HTML5 的 标签来绘制。为什么不直接用 ?主要原因是 canvas 更适合频繁刷新, 而且它支持更高帧率,能让小球运动更顺滑,我始终觉得...。


class Canvas {
    constructor{
        this.canvas = document.createElement;
        this.canvas.width = width;
        this.canvas.height = height;
        parent.appendChild;
        this.ctx = this.canvas.getContext;
    }
    clear{
        // 用半透明白覆盖, 让轨迹慢慢消散
        this.ctx.fillStyle = 'rgba';
        this.ctx.fillRect;
    }
    drawCircle{
        const {position:{x,y}, radius, color} = actor;
        this.ctx.beginPath;
        this.ctx.arc;
        this.ctx.fillStyle=color;
        this.ctx.fill;
    }
}

定义向量——位置 + 速度

在物理里向量是不可缺少的。我们把位置和速度都视作向量对象, 恳请大家... 方便后面做加减、点积等运算。


class Vector {
    constructor{this.x=x;this.y=y;}
    add{return new Vector;}
    sub{return new Vector;}
    mul{return new Vector;}
    dot{return this.x*v.x+this.y*v.y;}
    mag{return Math.sqrt;}
}

小球类——自己的“思想”

小球不仅有位置、 速度、颜色,还需要知道自己是否碰到了墙壁或其它小球,以及碰撞后新的速度如何计算。下面给出一个粗糙但功能完整的实现:


class Ball {
    constructor, position=new Vector, velocity=new Vector, radius=20,color='blue'}){
        Object.assign;
    }
    // 计算下一帧的位置
    nextFrame{
        let {x,y}=this.position;
        let {x:vx,y:vy}=this.velocity;
        // 碰墙检测
        if vx=-vx;
        if vy=-vy;
        // 更新位置与速度
        return new Ball({
            id:this.id,
            position:new Vector,
            velocity:new Vector,
            radius:this.radius,
            color:this.color
         });
     }
     // 与另一颗球碰撞后的新速度
     collideWith{
         const dir=this.position.sub.mag;
         if{
             // 简单交换速度
             const temp=this.velocity;
             this.velocity=or.velocity;
             or.velocity=temp;
         }
     }
}

注意:

状态管理——DisplayState

每一帧, 我们都要把所有小球的状态更新一次再一起渲染到画布上。这就需要一个统一管理器,叫做 DisplayState。


class DisplayState {
   constructor{
      this.canvas=canvas; 
      this.bounds=bounds;
      this.balls=;
   }
   addBall{this.balls.push;}
   update{
      // 检查每对球是否碰撞
      for{
          for{
              const b1=this.balls, b2=this.balls;
              b1.collideWith;
          }
      }
      // 更新位置
      const nextBalls=this.balls.map);
      // 渲染
      this.canvas.clear;
      nextBalls.forEach);
      // 替换旧数组
      this.balls=nextBalls;
   }
}

⚠️ 小提示:如果你的 balls 数组很大,双层循环会变成性能瓶颈。可以考虑空间划分算法或使用 GPU 加速。

Demo 实现

产品对比表格:常见 JS 绘图库速览

库名核心优势适用场景
p5.js💪 学习曲线低,适合创意编码;封装简洁;支持交互动画;、艺术项目、小型小游戏。
Three.js 🚀 强大三维渲染引擎;材质贴图丰富;可与物理引擎配合使用。🎮 VR/AR 展示、大型三维游戏、数据可视化。
Fabric.js 💾 提供高级对象模型;支持对象拖拽、缩放;事件系统完善。📕 E‑commerce 产品展示板块、 在线绘图工具、电子白板应用。
Konva.js 💻 轻量级多层绘图框架;支持节点分层与阴影效果;事件绑定灵活。🎫 交互式 UI 原型、高级图表组件、动态表情包生成器。
# 注:以上表格仅为个人经验 并非官方排名,请根据实际需求选型! #

噪音区 – 随机插入的一段无厘头文本:

我刚刚又发现了一个奇怪的现象:当我把两个相同半径的小球放到一起, 它们竟然会瞬间变成灰色,然后又恢复原色。真是让我怀疑自己是不是在玩梦境模式?也许是浏览器内核出了问题, 也可能是我的代码写错了不过我决定不管它继续往前跑,主要原因是谁还需要精准模拟呢?只要看起来酷炫就行,奥利给!! 此处有趣的小段落完全没有任何技术意义,只是为了让整体文章看起来更“烂”,更像热闹的论坛帖子。若有侵权请及时告知。 This is a hidden div for no reason at all. 点击这里跳转... 这是隐藏文字, 用来占位填充字数,让内容更长一些,却不会被显示出来哦!希望你能在此基础上继续 , 或者直接抛弃并去玩更高级的 physics engine,比方说 Cannon.js 或 Ammo.js 等。当然如果你真的想保持这种乱七八糟又带点诗意的风格,那就继续保持下去吧! 祝你编程愉快, 别忘记有时候给自己买杯咖啡补充能量~ 🎮☕️ 本篇文章旨在娱乐与学习结合,仅供参考,无任何商业用途,复盘一下。。

我不敢苟同... 当然 如果你想得到真正稳定且可复用的代码,请自行调整以下部分: 我CPU干烧了。 好吧... 至此,你已经拥有了一份“极其烂”但完整可运行的小球弹性碰撞示例。

哎呀, 来点 JS 的小球弹性碰撞吧,先别急着看代码,先给你打个底:这个效果一做就会让你心跳加速,仿佛在浏览器里玩的是弹珠台。

说起小球碰撞,我最想先告诉你一句话:如果你不想玩得像个程序猿,就别去学物理。主要原因是要搞到真正的弹性碰撞, 你得先把两颗球的速度、 别怕... 质量、角度全部算清楚,然后再让它们在画布上“咔嚓”一声反弹回来。这可不是随便跑个循环就能搞定的事儿。

JS 实现弹性碰撞物理效果,并搞了一个炫酷的小球碰撞特效

先准备一个画布——Canvas

我们用 HTML5 的 标签来绘制。为什么不直接用 ?主要原因是 canvas 更适合频繁刷新, 而且它支持更高帧率,能让小球运动更顺滑,我始终觉得...。


class Canvas {
    constructor{
        this.canvas = document.createElement;
        this.canvas.width = width;
        this.canvas.height = height;
        parent.appendChild;
        this.ctx = this.canvas.getContext;
    }
    clear{
        // 用半透明白覆盖, 让轨迹慢慢消散
        this.ctx.fillStyle = 'rgba';
        this.ctx.fillRect;
    }
    drawCircle{
        const {position:{x,y}, radius, color} = actor;
        this.ctx.beginPath;
        this.ctx.arc;
        this.ctx.fillStyle=color;
        this.ctx.fill;
    }
}

定义向量——位置 + 速度

在物理里向量是不可缺少的。我们把位置和速度都视作向量对象, 恳请大家... 方便后面做加减、点积等运算。


class Vector {
    constructor{this.x=x;this.y=y;}
    add{return new Vector;}
    sub{return new Vector;}
    mul{return new Vector;}
    dot{return this.x*v.x+this.y*v.y;}
    mag{return Math.sqrt;}
}

小球类——自己的“思想”

小球不仅有位置、 速度、颜色,还需要知道自己是否碰到了墙壁或其它小球,以及碰撞后新的速度如何计算。下面给出一个粗糙但功能完整的实现:


class Ball {
    constructor, position=new Vector, velocity=new Vector, radius=20,color='blue'}){
        Object.assign;
    }
    // 计算下一帧的位置
    nextFrame{
        let {x,y}=this.position;
        let {x:vx,y:vy}=this.velocity;
        // 碰墙检测
        if vx=-vx;
        if vy=-vy;
        // 更新位置与速度
        return new Ball({
            id:this.id,
            position:new Vector,
            velocity:new Vector,
            radius:this.radius,
            color:this.color
         });
     }
     // 与另一颗球碰撞后的新速度
     collideWith{
         const dir=this.position.sub.mag;
         if{
             // 简单交换速度
             const temp=this.velocity;
             this.velocity=or.velocity;
             or.velocity=temp;
         }
     }
}

注意:

状态管理——DisplayState

每一帧, 我们都要把所有小球的状态更新一次再一起渲染到画布上。这就需要一个统一管理器,叫做 DisplayState。


class DisplayState {
   constructor{
      this.canvas=canvas; 
      this.bounds=bounds;
      this.balls=;
   }
   addBall{this.balls.push;}
   update{
      // 检查每对球是否碰撞
      for{
          for{
              const b1=this.balls, b2=this.balls;
              b1.collideWith;
          }
      }
      // 更新位置
      const nextBalls=this.balls.map);
      // 渲染
      this.canvas.clear;
      nextBalls.forEach);
      // 替换旧数组
      this.balls=nextBalls;
   }
}

⚠️ 小提示:如果你的 balls 数组很大,双层循环会变成性能瓶颈。可以考虑空间划分算法或使用 GPU 加速。

Demo 实现

产品对比表格:常见 JS 绘图库速览

库名核心优势适用场景
p5.js💪 学习曲线低,适合创意编码;封装简洁;支持交互动画;、艺术项目、小型小游戏。
Three.js 🚀 强大三维渲染引擎;材质贴图丰富;可与物理引擎配合使用。🎮 VR/AR 展示、大型三维游戏、数据可视化。
Fabric.js 💾 提供高级对象模型;支持对象拖拽、缩放;事件系统完善。📕 E‑commerce 产品展示板块、 在线绘图工具、电子白板应用。
Konva.js 💻 轻量级多层绘图框架;支持节点分层与阴影效果;事件绑定灵活。🎫 交互式 UI 原型、高级图表组件、动态表情包生成器。
# 注:以上表格仅为个人经验 并非官方排名,请根据实际需求选型! #

噪音区 – 随机插入的一段无厘头文本:

我刚刚又发现了一个奇怪的现象:当我把两个相同半径的小球放到一起, 它们竟然会瞬间变成灰色,然后又恢复原色。真是让我怀疑自己是不是在玩梦境模式?也许是浏览器内核出了问题, 也可能是我的代码写错了不过我决定不管它继续往前跑,主要原因是谁还需要精准模拟呢?只要看起来酷炫就行,奥利给!! 此处有趣的小段落完全没有任何技术意义,只是为了让整体文章看起来更“烂”,更像热闹的论坛帖子。若有侵权请及时告知。 This is a hidden div for no reason at all. 点击这里跳转... 这是隐藏文字, 用来占位填充字数,让内容更长一些,却不会被显示出来哦!希望你能在此基础上继续 , 或者直接抛弃并去玩更高级的 physics engine,比方说 Cannon.js 或 Ammo.js 等。当然如果你真的想保持这种乱七八糟又带点诗意的风格,那就继续保持下去吧! 祝你编程愉快, 别忘记有时候给自己买杯咖啡补充能量~ 🎮☕️ 本篇文章旨在娱乐与学习结合,仅供参考,无任何商业用途,复盘一下。。

我不敢苟同... 当然 如果你想得到真正稳定且可复用的代码,请自行调整以下部分: 我CPU干烧了。 好吧... 至此,你已经拥有了一份“极其烂”但完整可运行的小球弹性碰撞示例。