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

在本系列的第1部分(https://andsky.com/tech/tutorials/js-canvas-animations-linear-motion)中,我们讨论了将可重复使用的对象渲染到画布的基本知识,使用我们的GUI来提供更直观的控制,并通过我们的动画循环创建基本运动的幻觉。

牛仔板

我们可以将我们的项目从第一部分作为我们大多数动画的起点。

 1[label index.html]
 2<!DOCTYPE html>
 3<html lang="en">
 4  <head>
 5    <meta charset="UTF-8"/>
 6    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
 7    <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
 8    <title>HTML Canvas</title>
 9  </head>
10  <body>
11
12    <canvas></canvas>
13
14  </body>
15  <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.6/dat.gui.min.js"></script>
16  <script src="./canvas.js"></script>
17</html>
 1[label canvas.js]
 2// Get canvas element
 3const canvas = document.querySelector('canvas');
 4const c = canvas.getContext('2d');
 5
 6// Make canvas fullscreen
 7canvas.width = innerWidth;
 8canvas.height = innerHeight;
 9addEventListener('resize', () => {
10  canvas.width = innerWidth;
11  canvas.height = innerHeight;
12});
13
14// Control Panel
15const gui = new dat.GUI();
16
17const controls = {
18  dx: 0,
19  dy: 0,
20};
21
22gui.add(controls, 'dx', 0, 10);
23gui.add(controls, 'dy', 0, 10);
24
25// New Object
26class Ball {
27  constructor(x, y, radius, color) {
28    this.x = x;
29    this.y = y;
30    this.radius = radius;
31    this.color = color;
32  }
33}
34
35Ball.prototype.draw = function () {
36  c.beginPath();
37  c.fillStyle = this.color;
38  c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
39  c.fill();
40  c.closePath();
41};
42
43Ball.prototype.update = function () {
44  this.x += controls.dx;
45  this.y += -controls.dy;
46  this.draw();
47};
48
49const ball = new Ball(innerWidth / 2, innerHeight / 2, 50, 'red');
50
51// Render new instances
52const init = () => ball.draw();
53
54// Handle changes
55const animate = () => {
56  requestAnimationFrame(animate);
57
58  c.clearRect(0, 0, canvas.width, canvas.height);
59
60  ball.update();
61};
62
63init();
64animate();

布朗克

你可以预览我们的最终结果(https://codepen.io/alligatorio/pen/xxxKjyJ)。

为了在碰撞时改变我们的行为,我们只需要在我们的更新方法中添加一个条件,这将改变球的行为,当它触及边界时,在这种情况下,扭转方向。

 1[label canvas.js]
 2Ball.prototype.update = function() {
 3  if (this.y + this.radius > canvas.height || this.y - this.radius < 0) {
 4    controls.dy = -controls.dy;
 5  }
 6  this.y -= controls.dy;
 7
 8  if (this.x + this.radius > canvas.width || this.x - this.radius < 0) {
 9    controls.dx = -controls.dx;
10  }
11  this.x += controls.dx;
12
13  this.draw();
14};

色彩

现在让我们添加一些更有趣的行为,当我们击中某些东西,如改变球的颜色. 首先,我们需要一系列的颜色来选择和一个函数随机为我们选择一个. 每当我们击中一个边界我们可以重新分配我们的颜色值一个新的随机。

我建議你檢查 Kuler以製作自己的顏色板。

 1// Returns a color between 0 and the length of our color array
 2const randomColor = colors => colors[Math.floor(Math.random() * colors.length)];
 3
 4const colors = [
 5  '#e53935',
 6  '#d81b60',
 7  '#8e24aa',
 8  '#5e35b1',
 9  '#3949ab',
10  '#1e88e5',
11  '#039be5',
12  '#00acc1',
13  '#00897b',
14  '#43a047',
15  '#ffeb3b',
16  '#ef6c00'
17];
18
19// Re-assign color on contact
20Ball.prototype.update = function () {
21  if (this.y + this.radius > canvas.height || this.y - this.radius < 0) {
22    this.color = randomColor(colors);
23    controls.dy = -controls.dy;
24  };
25  this.y -= controls.dy;
26
27  if (this.x + this.radius > canvas.width || this.x - this.radius < 0) {
28    this.color = randomColor(colors);
29    controls.dx = -controls.dx;
30  };
31  this.x += controls.dx;
32
33  this.draw();
34};
35
36// Make it start with a random color
37const newBall = new Ball(innerWidth / 2, innerHeight / 2, 50, randomColor(colors));

尾巴

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

 1const animate = () => {
 2  requestAnimationFrame(animate);
 3
 4  c.fillStyle = `rgba(33, 33, 33, ${-controls.tail / 10})`; // Lower opacity creates a longer tail
 5  c.fillRect(0, 0, canvas.width, canvas.height);
 6
 7  newBall.update();
 8};
 9
10// We also need to update our controls with some default values
11const controls = {
12  dx: 5,
13  dy: 5,
14  tail: -5
15};
16
17gui.add(controls, 'dx', 0, 10);
18gui.add(controls, 'dy', 0, 10);
19gui.add(controls, 'tail', -10, 0);

结论

同样,我们现在有一个非常基本的碰撞系统,有一些特殊效果. 在本系列的即将到来的第3部分,我们将使用这里涵盖的概念来创建这种动态(雨动画)(https://codepen.io/alligatorio/pen/JjjPvma)。

如果您遇到以下任何问题,可以在 Codepen上找到一个工作示例。

Published At
Categories with 技术
Tagged with
comments powered by Disqus