パーティクル同士をつないで幾何学的模様をつくります。
HTML部分
やっていることは点を移動させる。移動方向は最初にランダムに決めておく。近くに別の点があったらつなぐ。近ければ濃い直線を描画する。これだけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>パーティクルアニメーション</title> <meta name = "viewport" content = "width=device-width, initial-scale = 1.0"> </head> <body> <canvas id = "canvas"></canvas> <script src= "./index.js"></script> </body> </html> |
JavaScript部分
グローバル定数はこれだけです。
1 2 3 4 5 6 7 8 |
const CANVAS_WIDTH = 360; const CANVAS_HEIGHT = 480; const COUNT = 50; // 生成するパーティクルの数 const particles = []; // パーティクルオブジェクトを格納する配列 const $canvas = document.getElementById('canvas'); const ctx = $canvas.getContext('2d'); |
Particleクラスの定義
Particleクラスを定義します。コンストラクタの引数は生成された座標と移動速度です。Move関数で移動させますが、canvasの端にきたら反対側にワープさせます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
class Particle { constructor(x, y, vx, vy){ this.X = x; this.Y = y; this.VX = vx this.VY = vy; } Move(){ this.X += this.VX; this.Y += this.VY; if(this.X < 0) this.X = CANVAS_WIDTH; if(this.X > CANVAS_WIDTH) this.X = 0; if(this.Y < 0) this.Y = CANVAS_HEIGHT; if(this.Y > CANVAS_HEIGHT) this.Y = 0; } Draw(){ ctx.beginPath(); ctx.arc(this.X, this.Y, 3, 0, Math.PI * 2); ctx.fillStyle = '#fff'; ctx.fill(); } } |
ページが読み込まれたときの処理
ページが読み込まれたらパーティクルを生成します。初期座標は0以上CANVAS_WIDTHまたはCANVAS_HEIGHT未満です。移動速度は0.4以上0.8未満、移動方向もランダムに決めます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
window.onload = () => { $canvas.width = CANVAS_WIDTH; $canvas.height = CANVAS_HEIGHT; ctx.fillStyle = '#000'; ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); for(let i = 0; i < COUNT; i++){ const x = Math.random() * CANVAS_WIDTH; const y = Math.random() * CANVAS_HEIGHT; const v = 0.4 + 0.4 * Math.random(); const angle = Math.random() * 2 * Math.PI; particles.push(new Particle(x, y, v * Math.cos(angle), v * Math.sin(angle))); } update(); } |
更新処理
更新処理はパーティクルを移動させて描画します。またそれぞれのパーティクルの近く(距離80以内)に他のパーティクルがあるか調べます。見つかった場合は距離が離れていると薄く、近いときは濃く描画したいので-0.02 * distance + 1.6でアルファチャンネルを求め、1を超えたときは1を設定します。そして直線で結びます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
function update(){ particles.forEach(p => p.Move()); ctx.fillStyle = '#000'; ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); particles.forEach(p => p.Draw()); for(let i = 0; i < COUNT; i++){ for(let k = 0; k < COUNT; k++){ const distance2 = Math.pow(particles[i].X - particles[k].X, 2) + Math.pow(particles[i].Y - particles[k].Y, 2); if(distance2 < Math.pow(80, 2)){ ctx.beginPath(); ctx.moveTo(particles[i].X, particles[i].Y); ctx.lineTo(particles[k].X, particles[k].Y); const distance = Math.sqrt(distance2); let alpha = -0.02 * distance + 1.6; if(alpha > 1) alpha = 1; if(alpha < 0) alpha = 0; ctx.strokeStyle = `rgba(255, 255, 255, ${alpha})`; ctx.stroke(); } } } requestAnimationFrame(update); } |