@manufosela/animation-shootingstar

@manufosela/animation-shootingstar

A Lit 3 web component for shooting star animations using Canvas API

0
Shooting Stars

Shooting Stars

Click anywhere to create a shooting star and make a wish!

Theme Variations

Different background themes for various moods

Default (Purple Night)

Warm Desert Night

Ocean Night

Aurora Sky

Speed Variations

Different speeds for different effects

Slow and Majestic

Fast Meteor Shower

Interactive Configuration

Customize all parameters in real-time

Events will appear here...

Minimal Mode

Without background stars for cleaner look

Shooting Stars Only

Demo code (CodePen-ready HTML, CSS, JS)
HTML (html)
<!-- Wish Counter -->
  <div class="wish-counter">
    <div class="count" id="wish-count">0</div>
    <div class="label">Shooting Stars</div>
  </div>

  <!-- Hero Section -->
  <section class="hero">
    <animation-shootingstar
      id="hero-stars"
      frequency="1500"
      speed="12"
      trail-length="30"
      max-stars="6"
      trigger-on-click
    ></animation-shootingstar>
    <div class="hero-content">
      <h1>Shooting Stars</h1>
      <p>Click anywhere to create a shooting star and make a wish!</p>
      <button class="cta-btn" onclick="document.getElementById('hero-stars').trigger()">
        Trigger Star
      </button>
    </div>
  </section>

  <!-- Demo Sections -->
  <div class="demo-sections">
    <!-- Theme Variations -->
    <section class="section">
      <h2>Theme Variations</h2>
      <p>Different background themes for various moods</p>

      <div class="demo-grid">
        <div class="demo-card">
          <h3>Default (Purple Night)</h3>
          <animation-shootingstar
            frequency="2500"
            speed="10"
            trail-length="25"
          ></animation-shootingstar>
        </div>

        <div class="demo-card">
          <h3>Warm Desert Night</h3>
          <animation-shootingstar
            class="theme-warm"
            frequency="3000"
            speed="8"
            trail-length="30"
            id="warm-theme"
          ></animation-shootingstar>
        </div>

        <div class="demo-card">
          <h3>Ocean Night</h3>
          <animation-shootingstar
            class="theme-ocean"
            frequency="2000"
            speed="12"
            trail-length="20"
            id="ocean-theme"
          ></animation-shootingstar>
        </div>

        <div class="demo-card">
          <h3>Aurora Sky</h3>
          <animation-shootingstar
            class="theme-aurora"
            frequency="2500"
            speed="9"
            trail-length="28"
            id="aurora-theme"
          ></animation-shootingstar>
        </div>
      </div>
    </section>

    <!-- Speed Variations -->
    <section class="section">
      <h2>Speed Variations</h2>
      <p>Different speeds for different effects</p>

      <div class="demo-grid">
        <div class="demo-card">
          <h3>Slow and Majestic</h3>
          <animation-shootingstar
            frequency="4000"
            speed="5"
            trail-length="40"
            max-stars="3"
          ></animation-shootingstar>
        </div>

        <div class="demo-card">
          <h3>Fast Meteor Shower</h3>
          <animation-shootingstar
            frequency="800"
            speed="18"
            trail-length="15"
            max-stars="8"
          ></animation-shootingstar>
        </div>
      </div>
    </section>

    <!-- Interactive Configuration -->
    <section class="section">
      <h2>Interactive Configuration</h2>
      <p>Customize all parameters in real-time</p>

      <div class="interactive-section">
        <animation-shootingstar
          id="configurable"
          frequency="2000"
          speed="10"
          trail-length="25"
          max-stars="5"
          trigger-on-click
        ></animation-shootingstar>

        <div class="controls-panel">
          <div class="control-group">
            <label>Frequency: <span id="freq-value">2000</span>ms</label>
            <input type="range" id="freq-range" min="500" max="5000" value="2000">
          </div>

          <div class="control-group">
            <label>Speed: <span id="speed-value">10</span></label>
            <input type="range" id="speed-range" min="3" max="25" value="10">
          </div>

          <div class="control-group">
            <label>Trail Length: <span id="trail-value">25</span></label>
            <input type="range" id="trail-range" min="10" max="50" value="25">
          </div>

          <div class="control-group">
            <label>Max Stars: <span id="max-value">5</span></label>
            <input type="range" id="max-range" min="1" max="15" value="5">
          </div>

          <div class="control-group">
            <label>Color Presets</label>
            <div class="color-presets">
              <button class="color-preset" data-colors='["#ffffff","#fffacd","#e6e6fa","#add8e6","#ffdab9"]'>Default</button>
              <button class="color-preset" data-colors='["#ffd700","#ff8c00","#ff4500"]'>Warm</button>
              <button class="color-preset" data-colors='["#00ffff","#00bfff","#87ceeb"]'>Cool</button>
              <button class="color-preset" data-colors='["#ff69b4","#ff1493","#da70d6"]'>Pink</button>
              <button class="color-preset" data-colors='["#00ff00","#32cd32","#7cfc00"]'>Green</button>
            </div>
          </div>

          <div class="control-group buttons-group">
            <button class="primary" id="trigger-btn">Trigger Star</button>
            <button id="toggle-btn">Pause</button>
            <button class="danger" id="clear-btn">Clear All</button>
          </div>

          <div class="event-log" id="event-log">
            <p style="color: #666;">Events will appear here...</p>
          </div>
        </div>
      </div>
    </section>

    <!-- No Background Stars -->
    <section class="section">
      <h2>Minimal Mode</h2>
      <p>Without background stars for cleaner look</p>

      <div class="demo-card" style="max-width: 800px; margin: 0 auto;">
        <h3>Shooting Stars Only</h3>
        <animation-shootingstar
          show-background-stars="false"
          frequency="1500"
          speed="12"
          trail-length="35"
          style="--shootingstar-background: #000;"
        ></animation-shootingstar>
      </div>
    </section>
  </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;
      margin: 0;
      padding: 0;
    }

    .hero {
      position: relative;
      height: 100vh;
      display: flex;
      align-items: center;
      justify-content: center;
    }

    .hero animation-shootingstar {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
    }

    .hero-content {
      position: relative;
      z-index: 2;
      text-align: center;
      padding: 20px;
    }

    .hero h1 {
      font-size: 3rem;
      margin-bottom: 1rem;
      text-shadow: 0 0 30px rgba(255, 255, 255, 0.3);
    }

    .hero p {
      font-size: 1.2rem;
      color: var(--text-muted);
      margin-bottom: 2rem;
    }

    .hero .cta-btn {
      padding: 15px 40px;
      font-size: 1.1rem;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      border: none;
      color: white;
      border-radius: 30px;
      cursor: pointer;
      transition: transform 0.2s, box-shadow 0.2s;
    }

    .hero .cta-btn:hover {
      transform: scale(1.05);
      box-shadow: 0 10px 30px rgba(102, 126, 234, 0.4);
    }

    .demo-sections {
      padding: 60px 20px;
      max-width: 1400px;
      margin: 0 auto;
    }

    .section {
      margin-bottom: 80px;
    }

    .section h2 {
      text-align: center;
      margin-bottom: 10px;
      color: var(--text)acd;
      font-size: 1.8rem;
    }

    .section p {
      text-align: center;
      color: #888;
      margin-bottom: 30px;
    }

    .demo-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
      gap: 30px;
    }

    .demo-card {
      background: rgba(255, 255, 255, 0.02);
      border-radius: 16px;
      overflow: hidden;
      border: 1px solid rgba(255, 255, 255, 0.1);
    }

    .demo-card h3 {
      padding: 15px 20px;
      background: rgba(255, 255, 255, 0.05);
      margin: 0;
      font-size: 1rem;
      color: #add8e6;
    }

    .demo-card animation-shootingstar {
      display: block;
      height: 300px;
    }

    /* Interactive Demo */
    .interactive-section {
      background: rgba(255, 255, 255, 0.02);
      border-radius: 16px;
      overflow: hidden;
      border: 1px solid rgba(255, 255, 255, 0.1);
    }

    .interactive-section animation-shootingstar {
      height: 400px;
    }

    .controls-panel {
      padding: 25px;
      background: rgba(0, 0, 0, 0.3);
      display: flex;
      flex-wrap: wrap;
      gap: 25px;
      justify-content: center;
      align-items: flex-start;
    }

    .control-group {
      display: flex;
      flex-direction: column;
      gap: 8px;
      min-width: 150px;
    }

    .control-group label {
      font-size: 0.85rem;
      color: var(--text-muted);
    }

    .control-group input[type="range"] {
      width: 100%;
    }

    .control-group input[type="color"] {
      width: 100%;
      height: 35px;
      border: none;
      border-radius: 6px;
      cursor: pointer;
    }

    .buttons-group {
      display: flex;
      gap: 10px;
      flex-wrap: wrap;
      align-items: center;
    }

    button {
      padding: 10px 20px;
      background: rgba(173, 216, 230, 0.2);
      border: 1px solid #add8e6;
      color: #add8e6;
      border-radius: 6px;
      cursor: pointer;
      font-size: 0.9rem;
      transition: all 0.2s;
    }

    button:hover {
      background: rgba(173, 216, 230, 0.3);
    }

    button.primary {
      background: rgba(255, 250, 205, 0.2);
      border-color: var(--text)acd;
      color: var(--text)acd;
    }

    button.primary:hover {
      background: rgba(255, 250, 205, 0.3);
    }

    button.danger {
      background: rgba(255, 107, 107, 0.2);
      border-color: #ff6b6b;
      color: #ff6b6b;
    }

    button.danger:hover {
      background: rgba(255, 107, 107, 0.3);
    }

    .event-log {
      background: rgba(0, 0, 0, 0.4);
      padding: 15px;
      border-radius: 8px;
      max-height: 120px;
      overflow-y: auto;
      font-family: monospace;
      font-size: 0.8rem;
      min-width: 200px;
    }

    .event-log p {
      margin: 5px 0;
      color: #add8e6;
      text-align: left;
    }

    .color-presets {
      display: flex;
      gap: 8px;
      flex-wrap: wrap;
    }

    .color-preset {
      padding: 6px 12px;
      background: rgba(255, 255, 255, 0.1);
      border: 1px solid rgba(255, 255, 255, 0.2);
      border-radius: 4px;
      color: white;
      cursor: pointer;
      font-size: 0.75rem;
    }

    .color-preset:hover {
      background: rgba(255, 255, 255, 0.2);
    }

    /* Theme variants */
    .theme-warm {
      --shootingstar-background: linear-gradient(to bottom, #1a0a00 0%, #3d1a00 50%, #2d1000 100%);
    }

    .theme-ocean {
      --shootingstar-background: linear-gradient(to bottom, #000428 0%, #004e92 100%);
    }

    .theme-aurora {
      --shootingstar-background: linear-gradient(to bottom, #0f0c29 0%, #1a3a4a 50%, #0d2d0d 100%);
    }

    .wish-counter {
      position: fixed;
      bottom: 20px;
      right: 20px;
      background: rgba(0, 0, 0, 0.8);
      padding: 15px 25px;
      border-radius: 10px;
      border: 1px solid rgba(255, 250, 205, 0.3);
      text-align: center;
      z-index: 100;
    }

    .wish-counter .count {
      font-size: 2rem;
      color: var(--text)acd;
    }

    .wish-counter .label {
      font-size: 0.8rem;
      color: #888;
    }
JS (js)
import "https://esm.sh/@manufosela/animation-shootingstar";

    let wishCount = 0;

    // Update wish counter for all shooting stars
    document.querySelectorAll('animation-shootingstar').forEach(el => {
      el.addEventListener('star-created', () => {
        wishCount++;
        document.getElementById('wish-count').textContent = wishCount;
      });
    });

    // Custom colors for themed demos
    document.getElementById('warm-theme').colors = ['#ffd700', '#ff8c00', '#ff4500', '#ffb347'];
    document.getElementById('ocean-theme').colors = ['#00ffff', '#00bfff', '#87ceeb', '#e0ffff'];
    document.getElementById('aurora-theme').colors = ['#00ff00', '#00ffff', '#ff00ff', '#ffff00'];

    // Configurable demo controls
    const configurable = document.getElementById('configurable');
    const eventLog = document.getElementById('event-log');

    // Frequency
    document.getElementById('freq-range').addEventListener('input', (e) => {
      configurable.frequency = parseInt(e.target.value);
      document.getElementById('freq-value').textContent = e.target.value;
    });

    // Speed
    document.getElementById('speed-range').addEventListener('input', (e) => {
      configurable.speed = parseInt(e.target.value);
      document.getElementById('speed-value').textContent = e.target.value;
    });

    // Trail length
    document.getElementById('trail-range').addEventListener('input', (e) => {
      configurable.trailLength = parseInt(e.target.value);
      document.getElementById('trail-value').textContent = e.target.value;
    });

    // Max stars
    document.getElementById('max-range').addEventListener('input', (e) => {
      configurable.maxStars = parseInt(e.target.value);
      document.getElementById('max-value').textContent = e.target.value;
    });

    // Color presets
    document.querySelectorAll('.color-preset').forEach(btn => {
      btn.addEventListener('click', () => {
        const colors = JSON.parse(btn.dataset.colors);
        configurable.colors = colors;
        logEvent(`Colors changed: ${colors.length} colors`);
      });
    });

    // Trigger button
    document.getElementById('trigger-btn').addEventListener('click', () => {
      configurable.trigger();
    });

    // Toggle pause
    const toggleBtn = document.getElementById('toggle-btn');
    toggleBtn.addEventListener('click', () => {
      if (configurable.paused) {
        configurable.resume();
        toggleBtn.textContent = 'Pause';
      } else {
        configurable.pause();
        toggleBtn.textContent = 'Resume';
      }
    });

    // Clear button
    document.getElementById('clear-btn').addEventListener('click', () => {
      configurable.clear();
      logEvent('All stars cleared');
    });

    // Event logging
    configurable.addEventListener('star-created', (e) => {
      logEvent(`Star created at (${Math.round(e.detail.x)}, ${Math.round(e.detail.y)})`);
    });

    configurable.addEventListener('star-faded', () => {
      logEvent('Star faded out');
    });

    function logEvent(message) {
      const p = document.createElement('p');
      p.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
      eventLog.insertBefore(p, eventLog.firstChild);

      // Keep only last 10 events
      while (eventLog.children.length > 10) {
        eventLog.removeChild(eventLog.lastChild);
      }
    }