背景滚动碰撞小球-Matter.js

1,406 阅读5分钟

写在开头

哈喽呀!😃

今天是2024年6月11号,坏消息是端午假期刚结束,好消息是这周只要上4天,有盼头。👻

近来,广州一直下雨,人已发霉🤢!感觉这场雨好像下了好久好久,一个月?两个月?已经记不得了。。。

希望赶紧放晴吧🔆,快能养蘑菇了都🍄🍄🍄。

回到正题,本文将分享一篇关于 Matter.js 的内容,请诸君按需食用。

安装依赖

Matter.js 是一个 2D 网络物理引擎。

(听起来是不是很厉害的样子?简介越短,能力越强。💪)

确实很强,可以先瞧瞧示例:传送门

官方文档:传送门

安装依赖:

npm install matter-js matter-wrap

或者使用 CDN

<script src="https://unpkg.com/matter-js@0.19.0/build/matter.js"></script>
<script src="https://unpkg.com/matter-wrap@0.2.0/build/matter-wrap.js"></script>

这次咱们会使用到 matter-jsmatter-wrap 两个包,关于它们的一些细节使用情况这里就不过多介绍了,直接推荐掘友写得文章:

matter-js 参考文章:传送门

matter-wrap 参考文章:传送门

看完文章应该就能了解个大概哈😁,或者你直接看看官方文档也可以,主要就是了解 EngineRenderRunner 等几个关键对象,后面使用到小编也会写明详细注释的,咱们直接来看看具体实现过程,冲!

正文内容

先进行布局:

<!DOCTYPE html>
<html>
<head>
  <style>
    body {
      background-color: #f2f2ff;
    }
    canvas {
      position: fixed;
      z-index: -1;
      top: 0;
      left: 0;
    }
  </style>
</head>
<body>
  <div class="content">
    很长很长的内容........
  </div>
  <canvas id="bg"></canvas>
</body>
</html>

引入 matter-jsmatter-wrap 两个包,这里就直接使用 CDN 引入了:

<script src="https://unpkg.com/matter-js@0.19.0/build/matter.js"></script>
<script src="https://unpkg.com/matter-wrap@0.2.0/build/matter-wrap.js"></script>

初始化 matter-js 库,随便创建几个球玩玩:

<script>
window.addEventListener('DOMContentLoaded', () => {
  // 获取网页宽高
  const screenWidth = document.documentElement.clientWidth;
  const screenHeight = document.documentElement.clientHeight;
  // 创建引擎
  const engine = Matter.Engine.create();
  // y轴的重力设置成0,让小球固定在设置的位置上,否则会往下掉落
  engine.world.gravity.y = 0;
  // 创建渲染器
  let render = Matter.Render.create({
    canvas: document.getElementById('bg'),
    engine: engine,
    options: {
      width: screenWidth,
      height: screenHeight,
      background: 'transparent',
      // 使用实心填充渲染
      wireframes: false,
    }
  });
  // 执行渲染器
  Matter.Render.run(render);
  // 创建运行方法对象
  const runner = Matter.Runner.create();
  Matter.Runner.run(runner, engine);
  
  // 创建三个小球
  Matter.World.add(engine.world, [
    Matter.Bodies.circle(100, 100, 20),
    Matter.Bodies.circle(200, 200, 20),
    Matter.Bodies.circle(300, 300, 20),
  ]);
});
</script>

效果:

image.png

万事开头难,能搞懂这个小案例,接下来就是不断进行变种与扩展就行😉。

比如,我们让其生成一定数量的小球,小球的大小与颜色给它随机产生,使它更有趣一点。

// ...

// 全部小球
const balls = [];
// 小球数量
const total = 15;
for (let i = 0; i <= total; i++) {
  balls.push(createBall(screenWidth, screenHeight));
}
// 渲染全部小球
Matter.World.add(engine.world, balls);

/** @name 生成小球 **/
function createBall(width, height) {
  // 小球的大小
  const x = randomNumber(0, width);
  const y = randomNumber(0, height);
  const radius = randomNumber(30, 60);
  // 小球的颜色
  const colors = ['#8A2BE2', '#E74C3C', '#1E90FF', '#FFA500', '#FF007F'];
  const color = colors[Math.floor(randomNumber(0, colors.length))];

  // 生成一个小球
  return Matter.Bodies.circle(x, y, radius, {
    render: {
      fillStyle: color,
      opacity: 1
    },
    // 与空气的摩擦力,该值越高,物体碰撞后在空间中移动时减速得越快
    frictionAir: 0.03,
  });
}

/** @name 在一定范围内,生成随机数 **/
function randomNumber(min, max) {
  return Math.random() * (max - min) + min;
}

创建小球的API方法还支持另外两个参数,能帮我们填充小球的颜色、透明度等等功能,具体可以参考文档说明。

Matter.Bodies.circle(x, y, radius, [options], [maxSides])

参数 options 的详细信息:传送门

效果:

image.png

现在咱们有了小球,接下来就让它们在页面滚动的时候,也跟着运动起来,让它们去相互碰撞。😗

来看代码:

// ...

window.addEventListener('scroll', scrollThrottled);

// 垂直方向滚动的距离
let initPageYOffset = window.pageYOffset;
let timer = null;
/** @name 滚动监听进行节流 **/
function scrollThrottled() {
  if (!timer) timer = setTimeout(handleScroll, 100);
}
/** @name 滚动处理 **/
function handleScroll() {
  timer = null;
  // 根据滚动距离计算小球运动距离
  let delta = (initPageYOffset - window.pageYOffset) * 0.025;
  balls.forEach(ball => {
    // 重新设置小球的线速度
    Matter.Body.setVelocity(ball, {
      x: ball.velocity.x + delta * randomNumber(-0.5, 0.5),
      y: ball.velocity.y + delta * randomNumber(0.5, 1.5)
    });
  });
  initPageYOffset = window.pageYOffset;
}

上面,我们通过计算页面滚动的距离,为每个小球添加了适当的垂直速度,同时引入了一些随机性,让小球的运动看起来更加自然。此外,我们也为小球增加了一点点水平速度,进一步增强了它们的动态运动效果。

而给小球增加速度是使用了 setVelocity 方法,它的参数情况如下:

Matter.Body.setVelocity(body, velocity)

参数 velocity 的详细信息:传送门

061135.gif

现在小球能跟着页面滚动而运动了,只是仅仅只有十几个小球,滚动一下就没了?😓

这会就要使用到我们上面提到的第二个包:matter-wrap

它的作用是"在物体穿过边界时,物体将出现在边界的另一侧,同时保持其速度。"

简单来说,它能帮我们解决上面的问题,让我们的小球无穷无尽的出现。

且看:

<script>
  // 引入插件
  Matter.use('matter-wrap');
  
  window.addEventListener('DOMContentLoaded', () => {
    // ...
    /** @name 生成小球 **/
    function createBall(width, height) {
      // ...
      // 生成一个小球
      return Matter.Bodies.circle(x, y, radius, {
        render: {
          fillStyle: color,
          opacity: 1
        },
        frictionAir: 0.03,
        // 使用物体使用matter-wrap
        plugin: {
          wrap: {
            min: { x: 0, y: 0 },
            max: { x: width, y: height }
          }
        }
      });
    }
    // ...
});

使用起来很简单,就是配置到每个物体(小球)身上,让它们都使用 matter-wrap 规定边界的情况。

配置后,就能做到和开头的案例一样的效果啦。👻

当然,你也可以通过去修改 createBall 方法创建各种各样的图形,反正玩起来效果都挺不错,Interesting!

image.png image.png



至此,本篇文章就写完啦,撒花撒花。

image.png

希望本文对你有所帮助,如有任何疑问,期待你的留言哦。
老样子,点赞+评论=你会了,收藏=你精通了。