css 发射烟花的动画

动画1

主要功能:

真实的发射轨迹 - 烟花从底部发射到指定位置

物理引擎效果 - 包含重力、摩擦力、缓动函数

多彩爆炸效果 - 多种颜色的粒子爆炸

渐变消失 - 粒子逐渐变淡并缩小

交互控制 - 点击、按钮控制等多种方式
技术亮点:

面向对象设计 - 使用ES6类封装烟花逻辑

高性能渲染 - 使用requestAnimationFrame优化动画

真实物理模拟 - 重力、速度、摩擦力计算

视觉效果增强 - 发光效果、缩放动画、透明度变化

响应式设计 - 适配不同屏幕尺寸
控制方式:

点击屏幕 - 在点击位置发射烟花

发射烟花按钮 - 随机位置发射

自动发射按钮 - 开启连续发射模式

停止发射按钮 - 停止自动发射

清空画面按钮 - 清除所有烟花效果
烟花效果包括发射轨迹、爆炸粒子、重力下落、渐变消失等真实物理效果,营造出高质量的视觉体验。

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>高质量烟花动画</title>
  <style>
    body {
      margin: 0;
      padding: 0;
      background-color: #000;
      overflow: hidden;
      height: 100vh;
      display: flex;
      justify-content: center;
      align-items: center;
      font-family: Arial, sans-serif;
    }

    .fireworks-container {
      position: relative;
      width: 100%;
      height: 100%;
    }

    .firework {
      position: absolute;
      width: 5px;
      height: 5px;
      border-radius: 50%;
      pointer-events: none;
    }

    .controls {
      position: absolute;
      bottom: 20px;
      left: 50%;
      transform: translateX(-50%);
      z-index: 100;
      background: rgba(0, 0, 0, 0.7);
      padding: 15px;
      border-radius: 10px;
      color: white;
      text-align: center;
    }

    button {
      background: linear-gradient(45deg, #ff6b6b, #ffa502);
      border: none;
      color: white;
      padding: 10px 20px;
      margin: 5px;
      border-radius: 25px;
      cursor: pointer;
      font-weight: bold;
      transition: all 0.3s ease;
    }

    button:hover {
      transform: scale(1.05);
      box-shadow: 0 0 15px rgba(255, 255, 255, 0.5);
    }

    .title {
      position: absolute;
      top: 20px;
      left: 50%;
      transform: translateX(-50%);
      color: white;
      font-size: 2em;
      text-shadow: 0 0 10px #fff, 0 0 20px #fff, 0 0 30px #e60073, 0 0 40px #e60073;
      animation: glow 2s ease-in-out infinite alternate;
    }

    @keyframes glow {
      from {
        text-shadow: 0 0 10px #fff, 0 0 20px #fff, 0 0 30px #e60073, 0 0 40px #e60073;
      }

      to {
        text-shadow: 0 0 20px #fff, 0 0 30px #ff4da6, 0 0 40px #ff4da6, 0 0 50px #ff4da6;
      }
    }
  </style>
</head>

<body>
  <div class="title">🎆 高质量烟花秀 🎆</div>
  <div class="fireworks-container" id="fireworksContainer"></div>
  <div class="controls">
    <button onclick="launchFirework()">发射烟花</button>
    <button onclick="startAutoLaunch()">自动发射</button>
    <button onclick="stopAutoLaunch()">停止发射</button>
    <button onclick="clearFireworks()">清空画面</button>
  </div>

  <script>
    class Firework {
      constructor(container) {
        this.container = container;
        this.particles = [];
        this.colors = [
          '#ff0000', '#00ff00', '#0000ff', '#ffff00',
          '#ff00ff', '#00ffff', '#ff7700', '#ff0077',
          '#7700ff', '#00ff77', '#ffaa00', '#ff00aa'
        ];
      }

      launch(x, y) {
        // 创建烟花发射轨迹
        const rocket = document.createElement('div');
        rocket.className = 'firework';
        rocket.style.left = `${x}px`;
        rocket.style.top = `${window.innerHeight}px`;
        rocket.style.backgroundColor = this.getRandomColor();
        rocket.style.boxShadow = `0 0 10px ${this.getRandomColor()}`;

        this.container.appendChild(rocket);

        // 发射动画
        const startTime = Date.now();
        const duration = 1000 + Math.random() * 500;
        const targetY = y;

        const animateRocket = () => {
          const elapsed = Date.now() - startTime;
          const progress = Math.min(elapsed / duration, 1);

          // 使用缓动函数让运动更自然
          const easeOutQuad = 1 - Math.pow(1 - progress, 2);
          const currentY = window.innerHeight - (window.innerHeight - targetY) * easeOutQuad;

          rocket.style.top = `${currentY}px`;
          rocket.style.opacity = 1 - progress * 0.5;

          if (progress < 1) {
            requestAnimationFrame(animateRocket);
          } else {
            // 发射完成,爆炸效果
            this.explode(x, targetY);
            rocket.remove();
          }
        };

        requestAnimationFrame(animateRocket);
      }

      explode(x, y) {
        const particleCount = 100 + Math.floor(Math.random() * 50);

        for (let i = 0; i < particleCount; i++) {
          const particle = document.createElement('div');
          particle.className = 'firework';

          const angle = Math.random() * Math.PI * 2;
          const speed = 2 + Math.random() * 5;
          const size = 2 + Math.random() * 3;
          const color = this.getRandomColor();

          particle.style.width = `${size}px`;
          particle.style.height = `${size}px`;
          particle.style.left = `${x}px`;
          particle.style.top = `${y}px`;
          particle.style.backgroundColor = color;
          particle.style.boxShadow = `0 0 ${size * 2}px ${color}`;

          this.container.appendChild(particle);

          // 粒子动画参数
          const particleData = {
            element: particle,
            x: x,
            y: y,
            vx: Math.cos(angle) * speed,
            vy: Math.sin(angle) * speed,
            gravity: 0.05,
            friction: 0.98,
            life: 1.0,
            decay: 0.01 + Math.random() * 0.02,
            size: size
          };

          this.particles.push(particleData);
        }
      }

      updateParticles() {
        for (let i = this.particles.length - 1; i >= 0; i--) {
          const p = this.particles[i];

          // 更新位置
          p.x += p.vx;
          p.y += p.vy;
          p.vy += p.gravity;
          p.vx *= p.friction;
          p.vy *= p.friction;

          // 更新生命值
          p.life -= p.decay;

          // 更新元素位置和透明度
          p.element.style.left = `${p.x}px`;
          p.element.style.top = `${p.y}px`;
          p.element.style.opacity = p.life;
          p.element.style.transform = `scale(${p.life})`;

          // 移除死亡粒子
          if (p.life <= 0) {
            p.element.remove();
            this.particles.splice(i, 1);
          }
        }
      }

      getRandomColor() {
        return this.colors[Math.floor(Math.random() * this.colors.length)];
      }

      clear() {
        this.particles.forEach(p => p.element.remove());
        this.particles = [];
      }
    }

    // 全局变量
    const container = document.getElementById('fireworksContainer');
    const firework = new Firework(container);
    let autoLaunchInterval = null;

    // 点击发射烟花
    container.addEventListener('click', (e) => {
      firework.launch(e.clientX, e.clientY);
    });

    // 手动发射按钮
    function launchFirework() {
      const x = Math.random() * window.innerWidth;
      const y = Math.random() * window.innerHeight * 0.6;
      firework.launch(x, y);
    }

    // 自动发射
    function startAutoLaunch() {
      if (autoLaunchInterval) return;

      autoLaunchInterval = setInterval(() => {
        const x = Math.random() * window.innerWidth;
        const y = Math.random() * window.innerHeight * 0.6;
        firework.launch(x, y);
      }, 800);
    }

    // 停止自动发射
    function stopAutoLaunch() {
      if (autoLaunchInterval) {
        clearInterval(autoLaunchInterval);
        autoLaunchInterval = null;
      }
    }

    // 清空画面
    function clearFireworks() {
      firework.clear();
    }

    // 粒子更新循环
    function update() {
      firework.updateParticles();
      requestAnimationFrame(update);
    }

    // 启动更新循环
    update();

    // 页面加载完成后开始自动发射
    window.addEventListener('load', () => {
      setTimeout(() => {
        startAutoLaunch();
      }, 1000);
    });
  </script>
</body>

</html>

动画2

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>CSS烟花动画</title>
  <style>
    body {
      background-color: #000;
      overflow: hidden;
      height: 100vh;
      display: flex;
      justify-content: center;
      align-items: center;
    }

    .firework {
      position: absolute;
      width: 5px;
      height: 5px;
      border-radius: 50%;
      box-shadow: 0 0 10px 2px;
      animation: explode 1s ease-out forwards;
      opacity: 0;
    }

    @keyframes explode {
      0% {
        transform: translate(0, 0);
        opacity: 1;
      }

      100% {
        transform: translate(var(--tx), var(--ty));
        opacity: 0;
      }
    }

    .launch-area {
      position: absolute;
      bottom: 20%;
      width: 100%;
      text-align: center;
    }

    .launch-btn {
      padding: 10px 20px;
      background: linear-gradient(45deg, #ff3366, #ff9933);
      color: white;
      border: none;
      border-radius: 30px;
      font-size: 16px;
      cursor: pointer;
      box-shadow: 0 0 15px rgba(255, 51, 102, 0.7);
      transition: all 0.3s;
    }

    .launch-btn:hover {
      transform: scale(1.05);
      box-shadow: 0 0 20px rgba(255, 51, 102, 0.9);
    }
  </style>
</head>

<body>
  <div class="launch-area">
    <button class="launch-btn" onclick="launchFirework()">发射烟花</button>
  </div>

  <script>
    function launchFirework() {
      const colors = ['#ff3366', '#ff9933', '#33ccff', '#9933ff', '#33ff66'];
      const fireworkCount = 50;

      for (let i = 0; i < fireworkCount; i++) {
        const firework = document.createElement('div');
        firework.className = 'firework';

        // 随机颜色
        const color = colors[Math.floor(Math.random() * colors.length)];
        firework.style.backgroundColor = color;
        firework.style.boxShadow = `0 0 10px 2px ${color}`;

        // 随机位置偏移
        const angle = Math.random() * Math.PI * 2;
        const distance = 50 + Math.random() * 100;
        const tx = Math.cos(angle) * distance;
        const ty = Math.sin(angle) * distance;

        firework.style.setProperty('--tx', `${tx}px`);
        firework.style.setProperty('--ty', `${ty}px`);

        // 随机起始位置(在屏幕中央附近)
        const startX = 50 + (Math.random() - 0.5) * 20;
        const startY = 50 + (Math.random() - 0.5) * 20;

        firework.style.left = `${startX}%`;
        firework.style.top = `${startY}%`;

        // 随机动画延迟和持续时间
        firework.style.animationDelay = `${Math.random() * 0.2}s`;
        firework.style.animationDuration = `${0.5 + Math.random() * 0.5}s`;

        document.body.appendChild(firework);

        // 动画结束后移除元素
        firework.addEventListener('animationend', function () {
          firework.remove();
        });
      }
    }
  </script>
</body>

</html>
相关推荐
独泪了无痕1 小时前
深入浅析Vue3中的生命周期钩子函数
前端·vue.js
小白白一枚1111 小时前
vue和react的框架原理
前端·vue.js·react.js
字节逆旅1 小时前
从一次爬坑看前端的出路
前端·后端·程序员
若梦plus2 小时前
微前端之样式隔离、JS隔离、公共依赖、路由状态更新、通信方式对比
前端
若梦plus2 小时前
Babel中微内核&插件化思想的应用
前端·babel
若梦plus2 小时前
微前端中微内核&插件化思想的应用
前端
若梦plus2 小时前
服务化架构中微内核&插件化思想的应用
前端
若梦plus2 小时前
Electron中微内核&插件化思想的应用
前端·electron
若梦plus2 小时前
Vue.js中微内核&插件化思想的应用
前端