# 基本的动画

# 动画的基本步骤

你可以通过以下的步骤来画出一帧:

  • 清空 canvas 除非接下来要画的内容会完全充满 canvas(例如背景图),否则你需要清空所有。最简单的做法就是用 clearRect 方法。
  • 保存 canvas 状态 如果你要改变一些会改变 canvas 状态的设置(样式,变形之类的),又要在每画一帧之时都是原始状态的话,你需要先保存一下。
  • 绘制动画图形(animated shapes) 这一步才是重绘动画帧。
  • 恢复 canvas 状态 如果已经保存了 canvas 的状态,可以先恢复它,然后重绘下一帧。

# 操控动画

在 canvas 上绘制内容是用 canvas 提供的或者自定义的方法,而通常,我们仅仅在脚本执行结束后才能看见结果,比如说,在 for 循环里面做完成动画是不太可能的。

因此,为了实现动画,我们需要一些可以定时执行重绘的方法。

可以用window.setInterval(), window.setTimeout(),和window.requestAnimationFrame()来设定定期执行一个指定函数。

# 太阳系的动画

下面这个例子实现了一个经典的日地月三星系统。

const sun = new Image();
const moon = new Image();
const earth = new Image();
const WIDTH = 300;
const HEIGHT = 300;
const centerX = WIDTH / 2;
const centerY = HEIGHT / 2;
const sunR = 25;
const earthR = 12;
const moonR = 5;
const sunEarthDis = 105;
const earthMoonDis = 28.5;
function init() {
  sun.src = "https://static.gausszhou.top/data/image/learn/canvas/sun.png";
  moon.src =
    "https://static.gausszhou.top/data/image/learn/canvas/moon.png";
  earth.src =
    "https://static.gausszhou.top/data/image/learn/canvas/earth.png";
  window.requestAnimationFrame(draw);
}

function draw() {
  const ctx = document.getElementById("demo10_1").getContext("2d");

  ctx.globalCompositeOperation = "destination-over";
  ctx.clearRect(0, 0, WIDTH, HEIGHT); // clear canvas
  // draw sun
  ctx.drawImage(sun, centerX - sunR, centerY - sunR, sunR * 2, sunR * 2);
  ctx.fillStyle = "rgba(0,0,0,0.4)";
  ctx.strokeStyle = "rgba(0,153,255,0.4)";

  ctx.save();
  // move origin to center
  ctx.translate(centerX, centerY);
  // Earth
  var time = new Date();
  ctx.rotate(
    ((2 * Math.PI) / 60) * time.getSeconds() +
      ((2 * Math.PI) / 60000) * time.getMilliseconds()
  );
  ctx.translate(sunEarthDis, 0);
  ctx.drawImage(earth, -earthR, -earthR, earthR * 2, earthR * 2);
  // Shadow
  ctx.fillRect(0, -earthR, 150, earthR * 2);

  // Moon
  ctx.save();
  ctx.rotate(
    ((2 * Math.PI) / 5) * time.getSeconds() +
      ((2 * Math.PI) / 5000) * time.getMilliseconds()
  );
  ctx.translate(0, earthMoonDis);
  ctx.drawImage(moon, -moonR, -moonR, moonR * 2, moonR * 2);

  // restore coord
  ctx.restore();
  ctx.restore();

  // Earth orbit
  ctx.beginPath();
  ctx.arc(centerX, centerY, sunEarthDis, 0, Math.PI * 2, false);
  ctx.stroke();
  
  // call
  window.requestAnimationFrame(draw);
}

init();