このシリーズのパート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();

落とす

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

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();

single drop

ティッカー

それは素晴らしいことですが、上部に沿ってさらに多くのレンダリングが必要になります。 複数のドロップを作成するには、配列を作成し、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();
};

私たちが心配したい主なことは私たちの重力です。 重力によって下向きの動きが大きくなるため、これをupdateメソッドのdyに追加します。 したがって、負の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キャンバスについて学ぶことはまだたくさんありますが、この短いシリーズがその可能性を十分に穏やかに紹介したことを願っています。 ほとんどのサイトは多くの点で類似していますが、カスタムアニメーションを作成する機能は、最も人気のある自動サイトビルダーツールにはない独自性を提供します。