返回

MDN上的Canvas例子得到的启发

前端

MDN上有一个弹球的例子,可以让我们在屏幕上弹跳小球,当小球相互碰撞时,它们会改变颜色。这个例子非常有趣,它让我们可以直观地了解物理学原理在计算机图形学中的应用。

在本文中,我们将从这个例子出发,构建一个更复杂的程序。在这个程序中,小球不仅会相互碰撞,还会受到引力的影响。当两个小球发生碰撞时,它们会改变颜色,并根据牛顿第二定律计算出新的速度和位置。如果两个小球进入纠缠状态,需要第三个小球的碰撞来解散这种纠缠。

这个程序的代码如下:

// 创建画布
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');

// 设置画布大小
canvas.width = 500;
canvas.height = 500;

// 创建小球数组
const balls = [];

// 添加小球到数组中
for (let i = 0; i < 10; i++) {
  const ball = {
    x: Math.random() * canvas.width,
    y: Math.random() * canvas.height,
    vx: Math.random() * 10 - 5,
    vy: Math.random() * 10 - 5,
    radius: Math.random() * 10 + 5,
    color: `rgb(${Math.random() * 255}, ${Math.random() * 255}, ${Math.random() * 255})`
  };

  balls.push(ball);
}

// 绘制小球
function draw() {
  // 清空画布
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // 绘制小球
  for (let i = 0; i < balls.length; i++) {
    const ball = balls[i];

    ctx.beginPath();
    ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
    ctx.fillStyle = ball.color;
    ctx.fill();
  }
}

// 更新小球的位置和速度
function update() {
  // 更新小球的位置
  for (let i = 0; i < balls.length; i++) {
    const ball = balls[i];

    ball.x += ball.vx;
    ball.y += ball.vy;

    // 检测小球是否超出画布边界
    if (ball.x < 0 || ball.x > canvas.width) {
      ball.vx = -ball.vx;
    }

    if (ball.y < 0 || ball.y > canvas.height) {
      ball.vy = -ball.vy;
    }
  }

  // 检测小球之间的碰撞
  for (let i = 0; i < balls.length; i++) {
    const ball1 = balls[i];

    for (let j = i + 1; j < balls.length; j++) {
      const ball2 = balls[j];

      const dx = ball1.x - ball2.x;
      const dy = ball1.y - ball2.y;
      const distance = Math.sqrt(dx * dx + dy * dy);

      // 检测小球是否发生碰撞
      if (distance < ball1.radius + ball2.radius) {
        // 计算小球碰撞后的速度和位置
        const collisionAngle = Math.atan2(dy, dx);
        const mass1 = 1;
        const mass2 = 1;
        const v1x = ball1.vx * Math.cos(collisionAngle) + ball1.vy * Math.sin(collisionAngle);
        const v1y = ball1.vx * Math.sin(collisionAngle) - ball1.vy * Math.cos(collisionAngle);
        const v2x = ball2.vx * Math.cos(collisionAngle) + ball2.vy * Math.sin(collisionAngle);
        const v2y = ball2.vx * Math.sin(collisionAngle) - ball2.vy * Math.cos(collisionAngle);
        const v1xPrime = ((mass1 - mass2) / (mass1 + mass2)) * v1x + (2 * mass2 / (mass1 + mass2)) * v2x;
        const v1yPrime = ((mass1 - mass2) / (mass1 + mass2)) * v1y + (2 * mass2 / (mass1 + mass2)) * v2y;
        const v2xPrime = ((2 * mass1 / (mass1 + mass2)) * v1x + (mass2 - mass1) / (mass1 + mass2)) * v2x;
        const v2yPrime = ((2 * mass1 / (mass1 + mass2)) * v1y + (mass2 - mass1) / (mass1 + mass2)) * v2y;
        ball1.vx = v1xPrime * Math.cos(collisionAngle) - v1yPrime * Math.sin(collisionAngle);
        ball1.vy = v1xPrime * Math.sin(collisionAngle) + v1yPrime * Math.cos(collisionAngle);
        ball2.vx = v2xPrime * Math.cos(collisionAngle) - v2yPrime * Math.sin(collisionAngle);
        ball2.vy = v2xPrime * Math.sin(collisionAngle) + v2yPrime * Math.cos(collisionAngle);

        // 改变小球的颜色
        ball1.color = `rgb(${Math.random() * 255}, ${Math.random() * 255}, ${Math.random() * 255})`
        ball2.color = `rgb(${Math.random() * 255}, ${Math.random() * 255}, ${Math.random() * 255})`

        // 检测小球是否进入纠缠状态
        if (Math.abs(ball1.vx - ball2.vx) < 0.01 && Math.abs(ball1.vy - ball2.vy) < 0.01) {
          // 需要第三个小球的碰撞来解散这种纠缠
          for (let k = 0; k < balls.length; k++) {
            const ball3 = balls[k];

            if (ball3 !== ball1 && ball3 !== ball2) {
              // 计算小球3与小球1和2的距离
              const dx1 = ball3.x - ball1.x;
              const dy1 = ball3.y - ball1.y;
              const distance1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);

              const dx2 = ball3.x - ball2.x;
              const dy2 = ball3.y - ball2.y;
              const distance2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);

              // 如果小球3与小球1和2的距离都小于小球的半径,则可以解散纠缠
              if (distance1 < ball3.radius + ball1.radius && distance2 < ball3.radius + ball2.radius) {
                ball1.vx = Math.random() * 10 - 5;
                ball1.vy = Math.random() * 10 - 5;
                ball2.vx = Math.random() * 10 - 5;
                ball2.vy = Math.random() * 10 - 5;
                break;
              }
            }
          }
        }
      }
    }
  }
}

// 游戏循环
function gameLoop() {
  // 清空画布
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // 更新小球的位置和速度
  update();

  // 绘制小球
  draw();

  // 循环调用游戏循环函数
  requestAnimationFrame(gameLoop);
}

// 开始游戏
gameLoop();

当运行这个程序时,我们可以看到小球在屏幕上弹跳并相互作用。当小球发生碰撞时,它们会改变颜色。如果两个小球进入纠缠状态,需要第三个小球的碰撞来解散这种纠缠。