# 高级的动画

在上一章,我们制作了基本动画以及逐步了解了让物件移动的方法。在这一部分,我们将会对运动有更深的了解并学会添加一些符合物理的运动以让我们的动画更加高级。

# 绘制小球

我们将会画一个小球用于动画学习,所以首先在画布上画一个球。

const canvas = document.getElementById("demo12_1");
const ctx = canvas.getContext("2d");
const ball = {
  x: 100,
  y: 100,
  radius: 25,
  color: "blue",
  draw: function () {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
    ctx.closePath();
    ctx.fillStyle = this.color;
    ctx.fill();
  }
};
ball.draw();

# 添加速度和加速度

现在我们有了一个小球,正准备添加一些基本动画。

const canvas = document.getElementById("demo12_2");
const ctx = canvas.getContext("2d");
let raf;

const ball = {
  x: 100,
  y: 100,
  vx: 5,
  vy: 2,
  radius: 25,
  color: "blue",
  draw: function () {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
    ctx.closePath();
    ctx.fillStyle = this.color;
    ctx.fill();
  }
};

function animate() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ball.draw();
  ball.vy += 0.25;
  ball.x += ball.vx;
  ball.y += ball.vy;
  raf = window.requestAnimationFrame(animate);
}

canvas.addEventListener("mouseover", function (e) {
  raf = window.requestAnimationFrame(animate);
});

canvas.addEventListener("mouseout", function (e) {
  window.cancelAnimationFrame(raf);
});

ball.draw();

让我们看看现今它变得如何。移动你的鼠标到画布里开启动画。

WARNING

哦买噶,我们的球不见了。

# 添加边界

若没有任何的碰撞检测,我们的小球很快就会超出画布。我们需要检查小球的 x 和 y 位置是否已经超出画布的尺寸以及是否需要将速度矢量反转。

if (
  ball.y + ball.vy > canvas.height - ball.radius ||
  ball.y + ball.vy < ball.radius
) {
  ball.vy = -ball.vy;
}
if (
  ball.x + ball.vx > canvas.width - ball.radius ||
  ball.x + ball.vx < ball.radius
) {
  ball.vx = -ball.vx;
}
if (ball.y > canvas.height - ball.radius){
  ball.y = canvas.height - ball.radius;
}

# 添加阻尼

小球会一直运动下去,我们让小球每次碰撞都损失一定能量,就像这样。

if (
  ball.y + ball.vy > canvas.height - ball.radius ||
  ball.y + ball.vy < ball.radius
) {
  ball.vy = -ball.vy;
  ball.vy *= 0.99;
}
if (
  ball.x + ball.vx > canvas.width - ball.radius ||
  ball.x + ball.vx < ball.radius
) {
  ball.vx = -ball.vx;
  ball.vx *= 0.75
}
if (ball.y > canvas.height - ball.radius){
  ball.y = canvas.height - ball.radius;
}

# 长尾效果

现在,我们使用的是 clearRect 函数帮我们清除前一帧动画。若用一个半透明的 fillRect 函数取代之,就可轻松制作长尾效果。

// ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'rgba(255,255,255,0.3)';
ctx.fillRect(0,0,canvas.width,canvas.height);