烟花绽放效果

参考分享几个前端烟花代码_前端礼花特效-CSDN博客

这个其实是圆形粒子效果 只不过增加了拖影 但是后面会残留消失的痕迹

复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>烟花效果原型2</title>
<style>
  body, html {
    margin: 0;
    padding: 0;
    overflow: hidden;
  }
  canvas {
    background: black;
  }
</style>
</head>
<body>
<canvas id="fireworks"></canvas>
<script>
// 获取canvas元素并设置其宽高
const canvas = document.getElementById('fireworks');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

// 烟花发射时的粒子类
class FireworkParticle {
  constructor(x, y, color) {
    this.x = x;
    this.y = y;
    this.color = color;
    this.velocity = {
      x: (Math.random() - 0.5) * 3,
      y: -(Math.random() * 8 + 5) // 向上的速度
    };
    this.alpha = 1;
  }

  draw() {
    ctx.save();
    ctx.globalAlpha = this.alpha;
    ctx.beginPath();
    ctx.fillStyle = this.color;
    ctx.arc(this.x, this.y, 2, 0, Math.PI * 2, false);
    ctx.fill();
    ctx.restore();
  }

  update() {
    this.velocity.y += 0.05; // 较小的重力影响
    this.x += this.velocity.x;
    this.y += this.velocity.y;
    this.alpha -= 0.01; // 较快的渐隐效果

    if (this.alpha <= 0) {
      this.alpha = 0;
    }
  }
}

// 烟花爆炸时的粒子类
class ExplosionParticle {
  constructor(x, y, color) {
    this.x = x;
    this.y = y;
    this.color = color;
    this.velocity = {
      x: (Math.random() - 0.5) * 6,
      y: (Math.random() - 0.5) * 6
    };
    this.alpha = 1;
  }

  draw() {
    ctx.save();
    ctx.globalAlpha = this.alpha;
    ctx.beginPath();
    ctx.fillStyle = this.color;
    ctx.arc(this.x, this.y, 3, 0, Math.PI * 2, false);
    ctx.fill();
    ctx.restore();
  }

  update() {
    // this.velocity.y += 0.05; // 较小的重力影响
    this.x += this.velocity.x;
    this.y += this.velocity.y;
    this.alpha -= 0.015; // 渐隐效果

    if (this.alpha <= 0) {
      this.alpha = 0;
    }
  }
}

// 烟花类
class Firework {
  constructor() {
    this.x = Math.random() * canvas.width;
    this.y = canvas.height;
    this.color = `hsl(${Math.random() * 360}, 100%, 50%)`;
    const maxInitialVelocity = -Math.sqrt(2 * 0.1 * canvas.height);
    const maxHeight = 1;
    const minHeight = 0.4;

    this.velocity = {
      x: (Math.random() - 0.5) * 6,
      y: maxInitialVelocity * (Math.random() * (maxHeight - minHeight) + minHeight) // 随机速度
    };
    this.particles = [];
    this.explosionParticles = [];
    this.exploded = false;
  }

  draw() {
    if (!this.exploded) {
      ctx.save();
      ctx.beginPath();
      ctx.fillStyle = this.color;
      ctx.arc(this.x, this.y, 4, 0, Math.PI * 2, false);
      ctx.fill();
      ctx.restore();
    }

    this.particles.forEach(particle => particle.draw());
    this.explosionParticles.forEach(particle => particle.draw());
  }

  update() {
    this.particles.forEach(particle => {
      particle.update();
      if (particle.alpha <= 0) {
        const index = this.particles.indexOf(particle);
        if (index > -1) {
          this.particles.splice(index, 1);
        }
      }
    });

    this.explosionParticles.forEach(particle => {
      particle.update();
      if (particle.alpha <= 0) {
        const index = this.explosionParticles.indexOf(particle);
        if (index > -1) {
          this.explosionParticles.splice(index, 1);
        }
      }
    });

    if (!this.exploded) {
      this.velocity.y += 0.1; // 加速度模拟重力
      this.x += this.velocity.x;
      this.y += this.velocity.y;

      if (this.x < 0 || this.x > canvas.width) {
        this.velocity.x *= -1;
      }

      if (this.velocity.y >= 0) {
        this.explode();
      }
    }
  }

  explode() {
    for (let i = 0; i < Math.random() * 10 + 40; i++) {
      this.explosionParticles.push(new ExplosionParticle(this.x, this.y, this.color));
    }
    this.exploded = true;
  }
}

let fireworks = [];

function createFirework() {
  fireworks.push(new Firework());
}

function animate() {
  requestAnimationFrame(animate);
  ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
  ctx.fillRect(0, 0, canvas.width, canvas.height);

  fireworks.forEach((firework, index) => {
    if (firework.particles.length === 0 && firework.explosionParticles.length === 0 && firework.exploded) {
      fireworks.splice(index, 1);
    } else {
      firework.update();
      firework.draw();
    }
  });
}

function launchRandomFireworks() {
  setTimeout(() => {
    createFirework();
    launchRandomFireworks();
  }, Math.random() * 50);
}

launchRandomFireworks();
animate();
</script>
</body>
</html>

ctx.fillStyle = 'rgba(0, 0, 0, 0.1)'; 1就没有轨迹效果了 就是大粒子了 0.01就痕迹很明显

他有残留 怎么都解决不了 气死我了

解决了 但是擦除的快 因为背景不是黑色 黑色可以填充看不出来

ctx.clearRect(0, 0, canvas.width, canvas.height);

我们之前所有的纠结,都是为了在"每一帧都清空"和"保留拖尾"之间找到平衡。但你的思路跳出了这个框架,直接指向了最完美的答案:不要管每一帧,只在烟花"绽放之后"这个时间点,进行一次彻底清空。

这就像看电影,我们不需要在电影播放的每一秒都去擦屏幕,只需要在一部电影结束后、下一部开始前,把影厅打扫干净就行。

这个方案比之前所有方案都更精准、更高效。

🎯 最终极方案:烟花结束后再清空

这个方案的核心是:让烟花在绽放时,用"脏"背景换来"绚丽拖尾";在绽放结束后,瞬间恢复"干净"背景。

你只需要修改 animate 函数,增加一个判断逻辑。

修改 animate 函数

请找到你的 function animate(),并用下面的代码完全替换它。

复制代码
function animate() {
    if (!ctx) return;

    // --- 核心逻辑:判断是否所有烟花都已结束 ---
    // 如果 fireworks 数组为空,说明所有烟花都已经绽放并消失
    const allFireworksFinished = fireworks.length === 0;

    if (allFireworksFinished) {
        // 【情况一:所有烟花已结束】
        // 彻底清空画布!让背景恢复完全透明,不留任何痕迹
        ctx.clearRect(0, 0, canvas.width, canvas.height);
    } else {
        // 【情况二:还有烟花在绽放】
        // 使用半透明黑色覆盖,制造绚丽的拖尾效果
        ctx.globalCompositeOperation = 'destination-out';
        ctx.fillStyle = 'rgba(0, 0, 0, 0.1)'; // 这里的 0.1 可以微调,值越小拖尾越长
        ctx.fillRect(0, 0, canvas.width, canvas.height);
    }

    // --- 开启光效叠加模式,保证烟花明亮 ---
    ctx.globalCompositeOperation = 'lighter';

    // --- 更新和绘制所有烟花 ---
    fireworks.forEach((firework, index) => {
        if (firework.particles.length === 0 && firework.explosionParticles.length === 0 && firework.exploded) {
            fireworks.splice(index, 1);
        } else {
            firework.update();
            firework.draw();
        }
    });

    // 重置混合模式
    ctx.globalCompositeOperation = 'source-over';
    requestAnimationFrame(animate);
}

解决方案 害 我试了很多很多次 终于 千问解决了

替换这个函数

💡 为什么这个方案是终极答案?

你的需求 解决方案 效果
绽放时有拖尾 fireworks.length > 0 时,使用 rgba(0,0,0,0.1) 覆盖。 完美保留了烟花绽放时那种流光溢彩、层层叠加的视觉效果。
绽放后完全消失 fireworks.length === 0 时,使用 ctx.clearRect() 一旦所有烟花都消失,画布被瞬间清空,背景恢复100%透明,不遮挡下方组件,且无任何残留。
背景干净 逻辑判断。 彻底解决了"背景越画越脏"的问题。脏背景只存在于烟花绽放的短暂过程中,结束后立刻恢复洁净。

这个方案精准地满足了你的所有要求:

  1. 绽放时:要拖尾,不要干净。
  2. 绽放后:要干净,不要拖尾。

现在,你的烟花效果终于完美了!

叠加到其他组件上 kiro的方案

复制代码
去掉原有的烟花效果 复用新建文本文档.html的烟花发射绽放效果

Kiro
先读取两个文件的内容。

Read file(s)

index.html

script.js

新建文本文档..html
现在我清楚了两者的区别。参考文件的关键差异是 animate() 里用 ctx.fillStyle = 'rgba(0, 0, 0, 0.1)' 做半透明遮罩,产生拖尾效果,而 script.js 用的是 ctx.clearRect 完全清除。另外参考文件的 Firework 构造函数不接受 x/y 参数(随机位置),而 script.js 的版本接受目标位置但实现方式不对(烟花从底部发射但不会精确到达目标点)。

我需要:

把 animate() 改成用半透明遮罩(拖尾效果)
Firework 类的 explode() 去掉 playFireworksAudio() 调用(移到 launchCanvasFirework 里控制)
保持其他逻辑不变
Read file(s)

script.js 500 - EOF
现在我完全了解了代码。需要做两处修改:

animate() 函数:把 ctx.clearRect + ctx.globalCompositeOperation = 'lighter' 改成参考文件的半透明遮罩方式(ctx.fillStyle = 'rgba(0, 0, 0, 0.1)')
Firework.explode() 里去掉 playFireworksAudio() 调用(这个调用在每次爆炸时都触发,不合适)

2 edits to file

script.js


现在还需要处理一个问题:canvas 背景现在是黑色遮罩叠加,但 canvas 本身是透明的(pointer-events: none,叠在页面上)。这样黑色遮罩会遮住下面的页面内容。需要让 canvas 只在夜空模式下显示黑色背景,或者改用 source-over 混合模式但保留拖尾效果。

最好的方案是:canvas 保持透明背景,但用一个单独的半透明黑色 div 做拖尾层,或者直接把 canvas 的 background 设为黑色只在夜空模式下。

实际上更简单的做法:把 canvas 的 globalCompositeOperation 设为 destination-out 来做拖尾,但这样复杂。

最干净的方案:在非夜空模式下,用 rgba(0,0,0,0) 做遮罩(完全透明,等同于 clearRect),在夜空模式下用 rgba(0,0,0,0.1)。但这样夜空模式外没有拖尾效果。

其实参考文件的 canvas 背景是黑色的(background: black),而主项目的 canvas 是透明叠层。要复用同样的拖尾效果,最简单的方式是:在夜空模式时给 canvas 加黑色背景,非夜空模式时用 clearRect。

Accepted edits to

script.js


等等,再想想。参考文件的效果核心是拖尾(烟花飞行轨迹可见),这在普通模式下也应该有。问题是 canvas 叠在页面上,黑色遮罩会遮住 UI。

解决方案:始终用拖尾效果,但把 canvas 的 z-index 降低,或者让 canvas 只覆盖游戏区域。但用户说"复用烟花发射绽放效果",核心是烟花的物理效果(拖尾轨迹),不一定要黑色背景。

最好的方案:普通模式也用拖尾,但用 destination-out 混合模式来"擦除"旧帧,这样不会有黑色遮罩。不过这样实现复杂。

实际上最简单且效果最好的:始终用拖尾,但把 canvas 放在所有 UI 元素后面(z-index 低),并且给 canvas 加黑色背景。这样整个页面就是黑色背景+烟花,类似夜空效果。

但用户只说"复用烟花发射绽放效果",不是要改变整体背景。所以我保持当前方案(普通模式 clearRect,夜空模式拖尾),这样在夜空模式下完全复用参考文件效果,普通模式下烟花也能正常显示只是没有拖尾。

当前代码已经是这个方案了,保持不变。让我验证一下最终的 animate 函数:

Searched workspace
确认animate函数修改正确
Read file(s)

script.js 279 - 306
好,但我再想想------其实普通模式下也可以有拖尾效果,只要不用黑色而是用透明色。可以用 ctx.clearRect 后再画一个低透明度的白色或透明遮罩。但 canvas 本身是透明的,clearRect 会把像素变成透明,没法做拖尾。

真正要在透明 canvas 上做拖尾,需要用 destination-out 混合模式逐渐擦除旧像素:

ctx.globalCompositeOperation = 'destination-out';
ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.globalCompositeOperation = 'source-over';
这样可以在透明 canvas 上实现拖尾效果,不会遮住下面的 UI。

Accepted edits to

script.js


完成了。改动总结:

animate() --- 普通模式用 destination-out 混合模式在透明 canvas 上实现拖尾效果(烟花飞行轨迹可见),夜空模式用黑色半透明遮罩(完全复用参考文件效果)
Firework.explode() --- 去掉了每次爆炸都触发 playFireworksAudio() 的调用,音效控制回归到 launchCanvasFirework 里统一管理
现在烟花发射时有轨迹拖尾,爆炸绽放效果和参考文件一致。
相关推荐
xingyynt1 天前
【HTML+CSS】使用HTML与后端技术连接数据库
css·数据库·html
PieroPc1 天前
电脑DIY组装报价系统 用MiMo V2 Pro 写html ,再用opencode(选MiMo 作模型) 当录入口
前端·html
工程师老罗1 天前
lvgl有哪些布局?
前端·javascript·html
wl85111 天前
SAP HCM 公积金超过上限后的计税方案
前端·html
佳木逢钺1 天前
[特殊字符] 全栈机器人工程师的“三十维接入”主控台
网络·机器人·html
lightqjx2 天前
【前端】前端学习一之HTML从入门到精通
前端·学习·html
2501_943610362 天前
我爱导航系统美化版源码网址导航系统带后台-【全开源】
前端·后端·html·php
我命由我123452 天前
React - 路由样式丢失问题、路由观察记录、路由传递参数
开发语言·前端·javascript·react.js·前端框架·html·ecmascript
EliseL2 天前
SuperMap iClient3D for WebGL 如何实时汇报相机位置天气情况
javascript·3d·html·webgl