使用CanvasAPI制作动画-第2部分:基本碰撞

来自菜鸟教程
跳转至:导航、​搜索

在本系列的 第 1 部分 中,我们介绍了将可重用对象渲染到画布的基础知识,使用我们的 GUI 进行更直观的控制,并使用我们的动画循环创建基本运动的错觉。 在这一部分中,我们将熟悉使用一个简单的球创建碰撞效果,该球在撞击画布边界时会改变颜色。

样板

我们可以使用 Part 1 中的项目作为大多数动画的起点。

索引.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
    <title>HTML Canvas</title>
  </head>
  <body>

    <canvas></canvas>

  </body>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.6/dat.gui.min.js"></script>
  <script src="./canvas.js"></script>
</html>

画布.js

// Get canvas element
const canvas = document.querySelector('canvas');
const c = canvas.getContext('2d');

// Make canvas fullscreen
canvas.width = innerWidth;
canvas.height = innerHeight;
addEventListener('resize', () => {
  canvas.width = innerWidth;
  canvas.height = innerHeight;
});

// Control Panel
const gui = new dat.GUI();

const controls = {
  dx: 0,
  dy: 0,
};

gui.add(controls, 'dx', 0, 10);
gui.add(controls, 'dy', 0, 10);

// New Object
class Ball {
  constructor(x, y, radius, color) {
    this.x = x;
    this.y = y;
    this.radius = radius;
    this.color = color;
  }
}

Ball.prototype.draw = function () {
  c.beginPath();
  c.fillStyle = this.color;
  c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
  c.fill();
  c.closePath();
};

Ball.prototype.update = function () {
  this.x += controls.dx;
  this.y += -controls.dy;
  this.draw();
};

const ball = new Ball(innerWidth / 2, innerHeight / 2, 50, 'red');

// Render new instances
const init = () => ball.draw();

// Handle changes
const animate = () => {
  requestAnimationFrame(animate);

  c.clearRect(0, 0, canvas.width, canvas.height);

  ball.update();
};

init();
animate();

保镖

你可以在这里预览我们的最终结果

要改变我们在碰撞时的行为,我们只需要向我们的 update 方法添加一个条件,该条件将在球撞击边界时改变球的行为,在这种情况下,改变它的方向。 请记住,浏览器正在查看对象的中心以获取其位置,因此我们总是希望将半径包括在计算中。

画布.js

Ball.prototype.update = function() {
  if (this.y + this.radius > canvas.height || this.y - this.radius < 0) {
    controls.dy = -controls.dy;
  }
  this.y -= controls.dy;

  if (this.x + this.radius > canvas.width || this.x - this.radius < 0) {
    controls.dx = -controls.dx;
  }
  this.x += controls.dx;

  this.draw();
};

颜色

现在让我们在击球时添加一些更有趣的行为,比如改变球的颜色。 首先,我们需要一个可供选择的颜色数组和一个为我们随机选择一种颜色的函数。 每当我们遇到边界时,我们都可以将我们的颜色值重新分配给一个新的随机值。

我建议查看 Kuler 来制作自己的调色板。

// Returns a color between 0 and the length of our color array
const randomColor = colors => colors[Math.floor(Math.random() * colors.length)];

const colors = [
  '#e53935',
  '#d81b60',
  '#8e24aa',
  '#5e35b1',
  '#3949ab',
  '#1e88e5',
  '#039be5',
  '#00acc1',
  '#00897b',
  '#43a047',
  '#ffeb3b',
  '#ef6c00'
];

// Re-assign color on contact
Ball.prototype.update = function () {
  if (this.y + this.radius > canvas.height || this.y - this.radius < 0) {
    this.color = randomColor(colors);
    controls.dy = -controls.dy;
  };
  this.y -= controls.dy;

  if (this.x + this.radius > canvas.width || this.x - this.radius < 0) {
    this.color = randomColor(colors);
    controls.dx = -controls.dx;
  };
  this.x += controls.dx;

  this.draw();
};

// Make it start with a random color
const newBall = new Ball(innerWidth / 2, innerHeight / 2, 50, randomColor(colors));

尾巴

现在我们已经有了基本的功能,我们可以通过在它后面添加一个彩色尾巴来使它在视觉上更有趣。 我们可以通过移除 clearRect 并用深色 RGBA 值填充整个画布来做到这一点。 这会从穿过它的任何物体中产生“残留”效果,我们可以通过背景的不透明度来控制残留的强度。

const animate = () => {
  requestAnimationFrame(animate);

  c.fillStyle = `rgba(33, 33, 33, ${-controls.tail / 10})`; // Lower opacity creates a longer tail
  c.fillRect(0, 0, canvas.width, canvas.height);

  newBall.update();
};

// We also need to update our controls with some default values
const controls = {
  dx: 5,
  dy: 5,
  tail: -5
};

gui.add(controls, 'dx', 0, 10);
gui.add(controls, 'dy', 0, 10);
gui.add(controls, 'tail', -10, 0);

结论

就这样,我们现在有了一个非常基本的碰撞系统,并带有一些特殊效果。 在本系列即将发布的第 3 部分中,我们将使用此处介绍的概念来创建这个动态的 雨动画

如果您在后面遇到任何问题,Codepen 上提供了一个工作示例。 随意分叉并分享你所做的。