@manufosela/animation-explosion

@manufosela/animation-explosion

A Lit 3 web component for particle explosion effects on click/trigger using Canvas API

Animation Explosion Component

Click anywhere inside the demo areas to trigger explosions

Basic Explosion

Default settings - click anywhere to explode

Custom Colors

Green/blue color scheme with more particles

High Gravity

Particles fall quickly with high gravity

Fire Explosion

Red/orange/yellow fire colors

Configurable

Adjust settings with the controls below

Event Logging

Watch events fire in the log below

Programmatic Trigger

Trigger explosions via JavaScript

Click buttons below to trigger

Demo code (CodePen-ready HTML, CSS, JS)
HTML (html)
<h1>Animation Explosion Component</h1>
  <p class="subtitle">Click anywhere inside the demo areas to trigger explosions</p>

  <div class="demo-container">
    <!-- Basic Demo -->
    <div class="demo-card">
      <h2>Basic Explosion</h2>
      <p>Default settings - click anywhere to explode</p>
      <animation-explosion>
        <button class="demo-button btn-primary">Click Me!</button>
      </animation-explosion>
    </div>

    <!-- Custom Colors Demo -->
    <div class="demo-card">
      <h2>Custom Colors</h2>
      <p>Green/blue color scheme with more particles</p>
      <animation-explosion
        id="custom-colors"
        particle-count="50"
        duration="2000">
        <button class="demo-button btn-success">Eco Explosion</button>
      </animation-explosion>
    </div>

    <!-- High Gravity Demo -->
    <div class="demo-card">
      <h2>High Gravity</h2>
      <p>Particles fall quickly with high gravity</p>
      <animation-explosion
        particle-count="40"
        gravity="0.3"
        spread="180">
        <button class="demo-button btn-warning">Heavy Burst</button>
      </animation-explosion>
    </div>

    <!-- Fire Explosion Demo -->
    <div class="demo-card">
      <h2>Fire Explosion</h2>
      <p>Red/orange/yellow fire colors</p>
      <animation-explosion
        id="fire-explosion"
        particle-count="60"
        gravity="0.05"
        duration="2500">
        <button class="demo-button btn-fire">Fire!</button>
      </animation-explosion>
    </div>

    <!-- Configurable Demo -->
    <div class="demo-card">
      <h2>Configurable</h2>
      <p>Adjust settings with the controls below</p>
      <animation-explosion id="configurable">
        <button class="demo-button btn-primary">Test It</button>
      </animation-explosion>
      <div class="controls">
        <label>
          Particle Count: <span id="particle-value">30</span>
          <input type="range" id="particle-range" min="10" max="100" value="30">
        </label>
        <label>
          Gravity: <span id="gravity-value">0.1</span>
          <input type="range" id="gravity-range" min="0" max="0.5" step="0.05" value="0.1">
        </label>
        <label>
          Spread: <span id="spread-value">360</span>
          <input type="range" id="spread-range" min="30" max="360" value="360">
        </label>
      </div>
    </div>

    <!-- Event Logging Demo -->
    <div class="demo-card">
      <h2>Event Logging</h2>
      <p>Watch events fire in the log below</p>
      <animation-explosion id="event-demo">
        <button class="demo-button btn-success">Trigger</button>
      </animation-explosion>
      <div class="event-log" id="event-log"></div>
    </div>

    <!-- Programmatic Trigger Demo -->
    <div class="demo-card">
      <h2>Programmatic Trigger</h2>
      <p>Trigger explosions via JavaScript</p>
      <animation-explosion id="programmatic-demo" trigger-on-click="false">
        <p style="color: #888;">Click buttons below to trigger</p>
      </animation-explosion>
      <div class="trigger-buttons">
        <button class="trigger-btn" onclick="triggerAt('top-left')">Top Left</button>
        <button class="trigger-btn" onclick="triggerAt('center')">Center</button>
        <button class="trigger-btn" onclick="triggerAt('bottom-right')">Bottom Right</button>
      </div>
    </div>
  </div>
CSS (css)
:root {
  --bg: #0c0f14;
  --bg-elevated: #141923;
  --bg-panel: #171d28;
  --border: #262f3f;
  --text: #f4f6fb;
  --text-muted: #a7b0c2;
  --text-dim: #7d879b;
  --accent: #ff8a3d;
  --accent-strong: #ff6a00;
  --accent-soft: rgba(255, 138, 61, 0.16);
  --shadow: 0 20px 50px rgba(5, 8, 14, 0.45);
  --radius-lg: 22px;
  --radius-md: 14px;
  --radius-sm: 10px;
  --max-width: 1160px;
}

* {
      box-sizing: border-box;
    }

    h1 {
      text-align: center;
      margin-bottom: 10px;
    }

    .subtitle {
      text-align: center;
      color: #888;
      margin-bottom: 40px;
    }

    .demo-container {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
      gap: 30px;
      max-width: 1200px;
      margin: 0 auto;
    }

    .demo-card {
      background: rgba(255, 255, 255, 0.05);
      border-radius: 16px;
      padding: 20px;
      border: 1px solid rgba(255, 255, 255, 0.1);
    }

    .demo-card h2 {
      margin-top: 0;
      font-size: 1.2rem;
      color: #4ecdc4;
    }

    .demo-card p {
      color: var(--text-muted);
      font-size: 0.9rem;
      margin-bottom: 20px;
    }

    animation-explosion {
      display: block;
      height: 200px;
      background: rgba(0, 0, 0, 0.3);
      border-radius: 8px;
    }

    .demo-button {
      padding: 15px 30px;
      font-size: 16px;
      border: none;
      border-radius: 8px;
      cursor: pointer;
      transition: transform 0.2s, box-shadow 0.2s;
    }

    .demo-button:hover {
      transform: scale(1.05);
    }

    .demo-button:active {
      transform: scale(0.95);
    }

    .btn-primary {
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      color: white;
    }

    .btn-success {
      background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
      color: white;
    }

    .btn-warning {
      background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
      color: white;
    }

    .btn-fire {
      background: linear-gradient(135deg, #f12711 0%, #f5af19 100%);
      color: white;
    }

    .controls {
      margin-top: 20px;
      padding-top: 20px;
      border-top: 1px solid rgba(255, 255, 255, 0.1);
    }

    .controls label {
      display: block;
      margin-bottom: 10px;
      font-size: 0.9rem;
    }

    .controls input[type="range"] {
      width: 100%;
      margin-top: 5px;
    }

    .event-log {
      margin-top: 20px;
      padding: 10px;
      background: rgba(0, 0, 0, 0.3);
      border-radius: 8px;
      font-family: monospace;
      font-size: 0.8rem;
      max-height: 100px;
      overflow-y: auto;
    }

    .event-log p {
      margin: 5px 0;
      color: #4ecdc4;
    }

    #programmatic-demo {
      text-align: center;
    }

    .trigger-buttons {
      display: flex;
      gap: 10px;
      justify-content: center;
      margin-top: 15px;
    }

    .trigger-btn {
      padding: 8px 16px;
      background: rgba(255, 255, 255, 0.1);
      border: 1px solid rgba(255, 255, 255, 0.2);
      color: white;
      border-radius: 4px;
      cursor: pointer;
    }

    .trigger-btn:hover {
      background: rgba(255, 255, 255, 0.2);
    }
JS (js)
import "https://esm.sh/@manufosela/animation-explosion";

    // Set custom colors for specific demos
    document.getElementById('custom-colors').colors = ['#00ff87', '#60efff', '#00b4d8', '#0077b6'];
    document.getElementById('fire-explosion').colors = ['#ff4500', '#ff6b35', '#f7931e', '#ffc107', '#ffeb3b'];

    // Configurable demo
    const configurable = document.getElementById('configurable');
    const particleRange = document.getElementById('particle-range');
    const gravityRange = document.getElementById('gravity-range');
    const spreadRange = document.getElementById('spread-range');

    particleRange.addEventListener('input', (e) => {
      configurable.particleCount = parseInt(e.target.value);
      document.getElementById('particle-value').textContent = e.target.value;
    });

    gravityRange.addEventListener('input', (e) => {
      configurable.gravity = parseFloat(e.target.value);
      document.getElementById('gravity-value').textContent = e.target.value;
    });

    spreadRange.addEventListener('input', (e) => {
      configurable.spread = parseInt(e.target.value);
      document.getElementById('spread-value').textContent = e.target.value;
    });

    // Event logging demo
    const eventDemo = document.getElementById('event-demo');
    const eventLog = document.getElementById('event-log');

    eventDemo.addEventListener('explosion-start', (e) => {
      const p = document.createElement('p');
      p.textContent = `[${new Date().toLocaleTimeString()}] explosion-start at (${Math.round(e.detail.x)}, ${Math.round(e.detail.y)})`;
      eventLog.appendChild(p);
      eventLog.scrollTop = eventLog.scrollHeight;
    });

    eventDemo.addEventListener('explosion-end', () => {
      const p = document.createElement('p');
      p.textContent = `[${new Date().toLocaleTimeString()}] explosion-end`;
      p.style.color = '#f5576c';
      eventLog.appendChild(p);
      eventLog.scrollTop = eventLog.scrollHeight;
    });

    // Programmatic trigger
    window.triggerAt = function(position) {
      const el = document.getElementById('programmatic-demo');
      const rect = el.getBoundingClientRect();

      let x, y;
      switch(position) {
        case 'top-left':
          x = 50;
          y = 50;
          break;
        case 'center':
          x = rect.width / 2;
          y = rect.height / 2;
          break;
        case 'bottom-right':
          x = rect.width - 50;
          y = rect.height - 50;
          break;
      }

      el.explode(x, y);
    };