このシリーズのパート2で、画面の周りを跳ね返り、境界線に衝突すると色が変わるボールを作成しました。 次に、学習した内容を使用して、この雨のアニメーションを作成します。このアニメーションは、各ドロップがキャンバスの下部に当たると、パーティクルエフェクトを使用してドロップを動的にレンダリングします。

ボイラープレート

画面の下部に非常に近い場所で作業するため、水平スクロールバーを非表示にする必要があります。 overflow: hidden、少し暗くするので、目を焦がさないようにします。

index.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>
    <style>
      body {
        overflow: hidden;
        background-color: #1a202c;
      }
    </style>
  </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>
canvas.js
// Utilities
const randomNum = (min, max) => Math.floor(Math.random() * (max - min + 1) + min);
const randomColor = colors => colors[Math.floor(Math.random() * colors.length)];

// 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 = {
  count: 0,
  velocity: 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');

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

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

  ball.update();
};

animate();

落とす

メインドロップを機能させることから始めましょう。 各ドロップのすべての変数をオブジェクトとして保存し、画面に線を引き、値を追加するだけです。 y いつでも位置 update 下に移動させるために実行されます。

canvas.js
class Drop {
  constructor(x, y, dy, thickness, length, color) {
    this.x = x;
    this.y = y;
    this.dy = dy;
    this.thickness = thickness;
    this.length = length;
    this.color = color;
  }
};

Drop.prototype.draw = function () {
  c.beginPath();
  c.strokeStyle = this.color;
  c.lineWidth = this.thickness;
  c.moveTo(this.x, this.y);
  c.lineTo(this.x, this.y - this.length);
  c.stroke();
  c.closePath();
}

Drop.prototype.update = function () {
  this.y += this.dy;

  this.draw();
}

キャンバス画面の中央にレンダリングして、機能するかどうかを確認しましょう。

const drop = new Drop(innerWidth / 2, innerHeight / 2, 2, 5, 30, 'red');

const animate = () => {
  requestAnimationFrame(animate);
  c.clearRect(0, 0, canvas.width, canvas.height);

  drop.update();
};

animate();

ティッカー

それは素晴らしいことですが、上部に沿ってさらに多くのレンダリングが必要になります。 複数のドロップを作成するには、配列を作成するだけで、 for ループして各アイテムにランダムな値を割り当ててから配列にプッシュし、 forEach フレームごとにそれぞれを更新します。 しかし、 for ループはドロップを1回だけレンダリングし、すべてがキャンバスを通過してサイト外に移動します。 したがって、画面の下に移動するすべてのドロップを削除しながら、新しいドロップを継続的に追加するには、少しクリエイティブである必要があります。

これを行うには、フレームごとに1つずつカウントアップするティッカーを作成します。ある数値で完全に割り切れるたびに、配列に新しいドロップを追加します。 数字で割ると、新しいドロップがレンダリングされる頻度が制御されます。 それらを削除し、処理能力を節約するために、それらが下部を通過したときにアレイからそれらをスプライスします。

モジュロ演算子を使用する(%)数値を除算して、余りが0に等しいかどうかを確認できます。 したがって、値が高いほど、新しいドロップがレンダリングされる頻度は低くなります。

ここにいる間、彼らに色を付けましょう。 同じ色の異なる値をランダムな厚さと長さとともに使用すると、ある程度の深さの錯覚を与えるのに役立つことがわかりました。 カラーパレットについては、Kulerをチェックすることをお勧めします。

canvas.js
const colors = [ '#9C4AFF', '#8C43E6', '#7638C2', '#5E2C99', '#492378'];

let drops = [];
let ticker = 0;
const animate = () => {
  requestAnimationFrame(animate);
  // Try using the 'residue' effect from Part 2
  // c.fillStyle = 'rgba(33, 33, 33, .3)'; //Lower opacity creates a longer tail
  // c.fillRect(0, 0, canvas.width, canvas.height);
  c.clearRect(0, 0, canvas.width, canvas.height);

  drops.forEach((drop, index) => {
    drop.update();
    if (drop.y >= canvas.height) drops.splice(index, 1);
  });

  // Timing between drops
  ticker++;
  let count = controls.count === 0 ? 0 : randomNum(controls.count + 5, controls.count);
  if (ticker % count == 0) {
    const x = randomNum(0, innerWidth);
    const y = 0;
    const dy = controls.velocity === 0 ? 0 : randomNum(controls.velocity, controls.velocity + 10);
    const thickness = randomNum(3, 5);
    const length = randomNum(20, 50);

    drops.push(new Drop(x, y, dy, thickness, length, randomColor(colors)));
  };
};

液滴と重力

地面にぶつかったときにスプラッシュ効果を作成するには、重力のような効果があり、メインドロップから弧を描く小さな粒子が必要になります。 私たちの Droplet クラスは私たちのメインとかなり似ています Drop、液滴は線ではなく円になるため、いくつかの違いがあります。

canvas.js
class Droplet {
  constructor(x, y, dx, dy, radius, color) {
    this.x = x;
    this.y = y;
    this.dx = dx;
    this.dy = dy;
    this.radius = radius;
    this.color = color;
    this.gravity = .1;
  }
};

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

私たちが心配したい主なことは私たちの重力です。 重力によって下向きの動きが大きくなるので、これを追加します dyupdate 方法。 ですから、上に移動する多くの液滴を生成すると、 dy 値を設定し、フレームごとに重力値を追加すると、キャンバスを通過するまで速度が低下し、方向が逆になり、速度が上がります。 液滴だけで簡単な例を作成しました。ここで実験できます。

Droplet.prototype.update = function () {
  this.dy += this.gravity;
  this.y += this.dy;
  this.x += this.dx;

  this.draw();
};

そして、メインのドロップと同じように更新して削除します。

let droplets = [];
const animate = () => {
  droplets.forEach((droplet, index) => {
    droplet.update();
    if (droplet.y >= canvas.height) droplets.splice(index, 1);
  });
};

スプラッシュ

パーティクルの追加は実際には非常に簡単です。 for ループしてメインドロップ位置でそれらを生成し、残りのいくつかのランダムな値を渡します。 また、ドロップに重力を追加して、ドロップをもう少しリアルに落下させましょう。

canvas.js
class Drop {
  constructor(x, y, dy, thickness, length, color) {
    this.x = x;
    this.y = y;
    this.dy = dy;
    this.thickness = thickness;
    this.color = color;
    this.length = length;
    this.gravity = .4;
  }
};

Drop.prototype.update = function () {
  // Stops drops if velocity controller is set to 0
  if (this.dy > 0) this.dy += this.gravity;
  this.y += this.dy;

  // It runs splash over the whole length of the drop, to we'll narrow it down to the end.
  if (this.y > canvas.height - 100) this.splash(this.x, this.y + (this.length * 2));

  this.draw();
}

Drop.prototype.splash = function (x, y) {
  for (let i = 0; i < 5; i++) {
    const dx = randomNum(-3, 3);
    const dy = randomNum(-1, -5);
    const radius = randomNum(1, 3);

    droplets.push(new Droplet(x, y, dx, dy, radius, randomColor(colors)));
  };
};

結論

彼がHTMLキャンバスについて学ぶことはまだたくさんありますが、この短いシリーズがその可能性を十分に穏やかに紹介したことを願っています。 ほとんどのサイトは多くの点で類似していますが、カスタムアニメーションを作成する機能は、最も人気のある自動サイトビルダーツールにはない独自性を提供します。