本日0件のお問い合わせがありました。

✉️FIELDにお問い合わせ

デザイン・フロントエンド

fireworkのjsで遊んでみました。

画面下のパラメータを調整すると
花火が変わります。



<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>p5.js Fireworks</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <style>
    body {
      margin: 0;
      overflow: hidden;
      display: flex;
      flex-direction: column;
      align-items: center;
    }

    #hanabi {
      width: 100%;
      height: 600px;
      position: relative;
      background: black;
    }

    .control-panel {
      width: 100%;
      background: rgba(0, 0, 0, 0.8);
      color: white;
      padding: 10px;
      font-family: Arial, sans-serif;
      position: fixed;
      bottom: 0;
      left: 0;
      display: flex;
      flex-wrap: wrap;
      justify-content: center;
      align-items: center;
    }

    .control-group {
      margin: 5px 10px;
      text-align: center;
    }

    label {
      display: block;
      font-size: 14px;
    }

    input {
      width: 60px;
      padding: 5px;
      text-align: center;
    }
  </style>
</head>

<body>
  <div id="hanabi"></div>

  <div class="control-panel">
    <div class="control-group">
      <label>発射間隔</label>
      <input type="number" id="fireworkInterval" value="100" min="10" max="500">
    </div>

    <div class="control-group">
      <label>発射速度</label>
      <input type="number" id="launchSpeed" value="20" min="5" max="50">
    </div>

    <div class="control-group">
      <label>花火の大きさ</label>
      <input type="number" id="fireworkSize" value="10" min="5" max="30">
    </div>

    <div class="control-group">
      <label>花火の粒数</label>
      <input type="number" id="particleCount" value="50" min="10" max="200">
    </div>

    <div class="control-group">
      <label>爆発持続</label>
      <input type="number" id="explosionDuration" value="30" min="10" max="100">
    </div>
  </div>
</body>

<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>
<script>
  let canvas;
  let fireworkList = [];
  let starField = [];

  let fireworkInterval = 100;
  let launchSpeed = 20;
  let fireworkSize = 10;
  let particleCount = 50;
  let explosionDuration = 30;

  function windowResized() {
    let hanabiDiv = document.getElementById("hanabi");
    resizeCanvas(hanabiDiv.clientWidth, hanabiDiv.clientHeight);
    generateStars();
  }

  function setup() {
    let hanabiDiv = document.getElementById("hanabi");
    canvas = createCanvas(hanabiDiv.clientWidth, hanabiDiv.clientHeight);
    canvas.parent("hanabi");
    colorMode(RGB);
    frameRate(60);
    generateStars();

    document.getElementById('fireworkInterval').addEventListener('input', (e) => {
      fireworkInterval = parseInt(e.target.value);
    });
    document.getElementById('launchSpeed').addEventListener('input', (e) => {
      launchSpeed = parseInt(e.target.value);
    });
    document.getElementById('fireworkSize').addEventListener('input', (e) => {
      fireworkSize = parseInt(e.target.value);
    });
    document.getElementById('particleCount').addEventListener('input', (e) => {
      particleCount = parseInt(e.target.value);
    });
    document.getElementById('explosionDuration').addEventListener('input', (e) => {
      explosionDuration = parseInt(e.target.value);
    });
  }

  function draw() {
    background(0);
    renderStars();

    if (frameCount % fireworkInterval === 0) {
      fireworkList.push(new Firework(random(width), height, 0, launchSpeed, 0.98));
    }

    for (let i = fireworkList.length - 1; i >= 0; i--) {
      fireworkList[i].update();
      if (fireworkList[i].hasExploded()) {
        fireworkList.splice(i, 1);
      }
    }
  }

  class Firework {
    constructor(x, y, vx, vy, gravity) {
      this.frameCount = 0;
      this.stage = 0;
      this.explosionDelay = 0;

      this.colorR = random(80, 235);
      this.colorG = random(80, 235);
      this.colorB = random(80, 235);
      this.opacity = 255;

      this.x = x;
      this.y = y;

      this.size = fireworkSize;
      this.targetHeight = random(height / 6, height / 2);
      this.launchHeight = height - this.targetHeight;

      this.vx = vx;
      this.vy = vy;
      this.gravity = gravity;

      this.trail = [];
      this.explosionParticles = [];
    }

    hasExploded() {
      return this.stage === 2;
    }

    update() {
      if (this.stage === 0) {
        this.launch();
      } else if (this.stage === 1) {
        this.explode();
      }
    }

    launch() {
      this.y -= this.vy;
      this.vy *= 0.98;

      this.renderFirework(this.x, this.y, this.size);

      if (this.y < this.targetHeight) {
        this.stage = 1;
        this.createExplosion();
      }
    }

    createExplosion() {
      for (let i = 0; i < particleCount; i++) {
        let angle = random(TWO_PI);
        let speed = random(1, 5);
        let vx = cos(angle) * speed;
        let vy = sin(angle) * speed;
        this.explosionParticles.push(new Firework(this.x, this.y, vx, vy, 0.98));
      }
    }

    explode() {
      for (let i = this.explosionParticles.length - 1; i >= 0; i--) {
        let p = this.explosionParticles[i];
        p.x += p.vx;
        p.y += p.vy;
        p.vx *= 0.98;
        p.vy *= 0.98;
        p.vy += 0.02;
        p.size -= 0.05;
        p.opacity -= 4;
        this.renderFirework(p.x, p.y, p.size, p.opacity);

        if (p.opacity <= 0 || p.size <= 0) {
          this.explosionParticles.splice(i, 1);
        }
      }

      if (this.explosionParticles.length === 0) {
        this.stage = 2;
      }
    }

    renderFirework(x, y, size, opacity = this.opacity) {
      let c = color(this.colorR, this.colorG, this.colorB);
      c.setAlpha(opacity);
      fill(c);
      ellipse(x, y, size, size);
    }
  }

  function generateStars() {
    starField = [];
    for (let i = 0; i < 100; i++) {
      starField.push([random(width), random(height / 2), random(1, 4)]);
    }
  }

  function renderStars() {
    for (let s of starField) {
      fill(color(200, 200, 255, random(150, 200)));
      ellipse(s[0], s[1], s[2]);
    }
  }
</script>

</html>

1. HTML の構造

基本構造

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>p5.js Fireworks</title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<style> ... </style>
</head>
<body>
<div id="hanabi"></div> <!-- 花火を表示するエリア -->
<div class="control-panel"> ... </div> <!-- パラメータを調整するフォーム -->
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>
<script> ... </script>
</html>
  • #hanabi花火を表示するエリア
  • .control-panelユーザーが調整できるパラメータ入力フォーム
  • p5.js<script> で読み込み、JavaScript で花火を描画

2. CSS (デザインとレイアウト)

花火の表示エリア

#hanabi {
width: 100%;
height: 600px;
position: relative;
background: black;
}
  • width: 100%; height: 600px; → 幅100%、高さ600px
  • background: black;背景を黒に設定(夜空っぽく)

フォームの配置

.control-panel {
width: 100%;
background: rgba(0, 0, 0, 0.8); /* 半透明の黒 */
color: white;
padding: 10px;
font-family: Arial, sans-serif;
position: fixed;
bottom: 0;
left: 0;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}
  • position: fixed; bottom: 0;フォームを画面下部に固定
  • width: 100%;フォームが横幅いっぱいに広がる
  • background: rgba(0, 0, 0, 0.8);背景を半透明の黒
  • display: flex; flex-wrap: wrap;フォームの入力欄を横並びにし、折り返し可能に

3. JavaScript(p5.js のスケッチ)

p5.js を使って、花火の描画とパラメータ調整機能を実装

① 変数の定義

let canvas;
let fireworkList = [];
let starField = [];
let fireworkInterval = 100;
let launchSpeed = 20;
let fireworkSize = 10;
let particleCount = 50;
let explosionDuration = 30;
  • canvasp5.js のキャンバスオブジェクト
  • fireworkList現在描画されている花火のリスト
  • starField背景の星のデータ
  • fireworkInterval花火を打ち上げる間隔(フレーム数)
  • launchSpeed花火の打ち上げ速度
  • fireworkSize花火の大きさ
  • particleCount爆発時の粒の数
  • explosionDuration爆発の持続時間

② p5.js の setup() 関数

function setup() {
let hanabiDiv = document.getElementById("hanabi");
canvas = createCanvas(hanabiDiv.clientWidth, hanabiDiv.clientHeight);
canvas.parent("hanabi");
colorMode(RGB);
frameRate(60);
generateStars();
  • createCanvas(hanabiDiv.clientWidth, hanabiDiv.clientHeight);
    • #hanabi の幅と高さに合わせてキャンバスを作成
  • canvas.parent("hanabi");
    • キャンバスを #hanabi に埋め込む
  • colorMode(RGB);
    • 色を RGB モードに設定
  • frameRate(60);
    • 60FPS でアニメーションを描画
  • generateStars();
    • 背景の星をランダムに生成

③ フォームの入力値を取得

document.getElementById('fireworkInterval').addEventListener('input', (e) => {
fireworkInterval = parseInt(e.target.value);
});

各入力欄の値が変わるたびに fireworkInterval などの値を更新。


draw() 関数

function draw() {
background(0);
renderStars();

if (frameCount % fireworkInterval === 0) {
fireworkList.push(new Firework(random(width), height, 0, launchSpeed, 0.98));
}

for (let i = fireworkList.length - 1; i >= 0; i--) {
fireworkList[i].update();
if (fireworkList[i].hasExploded()) {
fireworkList.splice(i, 1);
}
}
}
  • background(0);背景を毎フレーム黒で塗りつぶし
  • renderStars();星を描画
  • frameCount % fireworkInterval === 0 で、一定間隔ごとに花火を打ち上げ
  • 打ち上げた花火は fireworkList に追加
  • 爆発後の花火は fireworkList から削除

Firework クラス(花火の動作)

class Firework {
constructor(x, y, vx, vy, gravity) {
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;
this.gravity = gravity;
this.stage = 0;
this.explosionParticles = [];
}

hasExploded() {
return this.stage === 2;
}

update() {
if (this.stage === 0) {
this.launch();
} else if (this.stage === 1) {
this.explode();
}
}

launch() {
this.y -= this.vy;
this.vy *= 0.98;
this.renderFirework(this.x, this.y, fireworkSize);

if (this.y < height / 2) {
this.stage = 1;
this.createExplosion();
}
}

createExplosion() {
for (let i = 0; i < particleCount; i++) {
let angle = random(TWO_PI);
let speed = random(1, 5);
let vx = cos(angle) * speed;
let vy = sin(angle) * speed;
this.explosionParticles.push(new Firework(this.x, this.y, vx, vy, 0.98));
}
}

explode() {
for (let i = this.explosionParticles.length - 1; i >= 0; i--) {
let p = this.explosionParticles[i];
p.x += p.vx;
p.y += p.vy;
p.vy += 0.02;
p.size -= 0.05;
this.renderFirework(p.x, p.y, p.size);

if (p.size <= 0) {
this.explosionParticles.splice(i, 1);
}
}
if (this.explosionParticles.length === 0) {
this.stage = 2;
}
}

renderFirework(x, y, size) {
fill(255, 100, 0);
ellipse(x, y, size, size);
}
}
  • launch() → 花火を上昇
  • explode() → 爆発時に粒を拡散
  • createExplosion() → 爆発時の粒を生成

便利な時代になりましたね。

Yamamoto Yuya

プロフェッショナルとしての高いスキルと知識を持ち、誠実さと責任感を大切にする。常に向上心を持ち、新たな挑戦にも積極的に取り組む努力家。