简介:这是一份面向前端初学者与进阶开发者的烟花特效代码合集,收录11套基于HTML、jQuery与CSS实现的烟花动画方案,可用于节日页面、活动庆祝场景或动画原理学习。压缩包共59个文件,约4.6MB,以js脚本、html页面、css样式、png图片及json数据为主,另含README说明文档,结构清晰便于逐套对照研究。已有2983人学习下载,热度较高。各套代码实现思路不同:有的借助HTML5 canvas逐帧绘制烟花轨迹与爆炸效果,有的通过jQuery绑定点击事件与定时器触发烟花秀,还有的利用CSS3的transform、transition与keyframes模拟升空、绽放与消散。读者可从中掌握Math随机数生成方向与速度、requestAnimationFrame流畅动画、DOM操作与事件处理、性能优化及响应式适配等关键技能,适合作为前端动画练手素材与教学参考。
1. 烟花代码合集到底能拿来干什么:从节日彩蛋到前端练手项目
每年跨年、春节、中秋前后,总有人翻出「烟花代码」这个词来搜。搜的人大致分两类:一类是运营、行政、老师,想给活动页、课件、大屏加个应景的动效;另一类是刚学完 HTML、CSS、JS 基础的前端新手,想找一个「看得见效果、又不至于太难」的练手项目。11 套烟花代码、多种实现方式的合集,正好同时喂饱这两种需求——它把基于 HTML、jQuery、CSS 的烟花实现拆成不同难度、不同技术路线的样本,你可以直接抄一个能跑的,也可以顺着源码把粒子系统、Canvas 绘制、CSS 动画这几条线摸一遍。
但这里有个反直觉的结论:烟花代码的难点从来不在「放烟花」,而在「怎么让几百上千个粒子同时动起来还不卡」。新手写的版本往往一放就掉帧,手机直接发烫;而合集里那些看起来更朴素的实现,反而因为用了 CSS 动画或 requestAnimationFrame 的节流技巧,跑得又稳又省电。所以这篇不是给你贴一堆代码就完事,而是把「11 套」背后的几条技术路线讲清楚:哪套适合塞进活动页、哪套适合当学习模板、哪套只能本地跑着玩,以及参数怎么调、坑在哪。
如果你只是想要一个能立刻用的烟花效果,看完第 2、3 章就能动手;如果你想借这个项目把 Canvas 和 CSS 动画吃透,第 4、5 章的参数拆解和排查清单更值得细读。下面按「先选路线、再动手、最后避坑」的顺序展开。
2. 三条主流实现路线:Canvas 粒子、CSS 动画、jQuery DOM 各适合谁
在动手抄任何一套之前,先搞清楚这 11 套代码大致落在哪条技术路线上。选错路线,后面调参数会非常痛苦。常见做法是把它们归成三类:Canvas 粒子型、纯 CSS 动画型、jQuery/DOM 操作型。三者的性能上限、可定制程度、上手难度差别很大。
2.1 Canvas 粒子型:性能最好,也最像「真烟花」
Canvas 路线的核心思路是:用一块<canvas>作为画布,每一帧清屏后重新绘制所有粒子,粒子有位置、速度、重力、生命周期四个基本属性。爆炸时生成一批粒子,每个粒子按初速度向外飞散,同时受重力影响逐渐下落,透明度随生命周期衰减,最后被回收。
这套逻辑的好处是粒子数量可以轻松上到几百甚至上千,因为绘制走的是 GPU 加速的 2D 上下文,比操作 DOM 快一个数量级。缺点是代码量偏大,需要自己管理粒子的生成、更新、销毁,新手第一次看容易懵。
// Canvas 烟花核心:粒子类 + 动画循环 class Particle { constructor(x, y, color) { this.x = x; this.y = y; // 初速度:随机角度 + 随机大小,决定爆炸形状 const angle = Math.random() * Math.PI * 2; const speed = Math.random() * 4 + 1; this.vx = Math.cos(angle) * speed; this.vy = Math.sin(angle) * speed; this.color = color; this.life = 1; // 生命周期,1 表示满血 this.decay = 0.015; // 每帧衰减速度,越大消失越快 } update() { this.x += this.vx; this.y += this.vy; this.vy += 0.05; // 重力加速度,让粒子下落 this.vx *= 0.99; // 空气阻力,让扩散变慢 this.life -= this.decay; } draw(ctx) { ctx.globalAlpha = this.life; // 透明度跟随生命周期 ctx.fillStyle = this.color; ctx.beginPath(); ctx.arc(this.x, this.y, 2, 0, Math.PI * 2); ctx.fill(); } }这段代码里几个参数直接决定观感:speed控制爆炸半径,0.05是重力,调大粒子掉得快、像礼花,调小则飘得久、像蒲公英;decay控制拖尾长度,越小尾巴越长。vx *= 0.99是空气阻力,去掉它粒子会一直匀速飞出去,看起来就很假。实际项目里我一般把粒子数控制在 150~300 之间,超过 500 在中低端手机上就开始掉帧。
2.2 纯 CSS 动画型:零 JS 依赖,适合塞进静态页
CSS 路线的思路完全不同:不逐帧计算,而是用@keyframes定义一条从中心向外扩散并淡出的动画,然后生成若干个带随机延迟、随机方向的元素去跑这条动画。浏览器会把动画交给合成层处理,性能其实不差,而且完全不需要 JS 参与。
/* 单个烟花粒子的扩散动画 */ @keyframes firework-burst { 0% { transform: translate(0, 0) scale(1); opacity: 1; } 100% { /* --dx / --dy 由内联样式注入,控制每个粒子的方向 */ transform: translate(var(--dx), var(--dy)) scale(0.2); opacity: 0; } } .spark { position: absolute; width: 6px; height: 6px; border-radius: 50%; animation: firework-burst 1.2s ease-out forwards; }这里的关键是--dx、--dy两个 CSS 变量,用内联样式给每个粒子注入不同的位移值,就能让同一套动画跑出放射状效果。forwards保证动画结束后停在最后一帧,不会闪回。CSS 路线的短板是粒子数量受限于 DOM 节点数,一般 60~100 个就到头了,再多浏览器布局压力会明显上升。它最适合的场景是活动页里放一两朵点缀,而不是满屏炸开。
2.3 jQuery/DOM 操作型:老项目兼容首选,但别指望性能
第三类是早年最常见的写法:用 jQuery 动态创建<div>,然后靠setInterval或animate()改left、top、opacity。这类代码在合集里通常占好几套,因为写起来直观,改颜色、改数量都方便。
但它的性能是三者里最差的。每创建一个粒子就是一个真实 DOM 节点,几百个节点同时改样式,浏览器要反复重排重绘。如果你的项目本身还在用 jQuery(比如一些后台管理系统、老活动页),拿它做个轻量烟花没问题;但如果是新项目,我建议直接用 Canvas 或 CSS 方案,别为了省事引入 jQuery。
提示:判断该选哪条路线,先问自己三个问题——粒子要不要超过 200 个?页面是不是纯静态?项目里有没有现成的 jQuery?三个答案基本就能定位到唯一路线。
3. 从零跑通一套 Canvas 烟花:最小可运行代码与四个必调参数
选定 Canvas 路线后,接下来把它跑起来。这一章给出一份最小可运行的完整代码,你复制到一个.html文件里双击就能看到效果,然后再逐个调参数感受变化。这也是合集里最值得当模板的那一套。
3.1 最小可运行结构:一个 canvas 加一个动画循环
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <title>Canvas 烟花最小示例</title> <style> html, body { margin: 0; height: 100%; background: #050510; overflow: hidden; } canvas { display: block; } </style> </head> <body> <canvas id="fw"></canvas> <script> const canvas = document.getElementById('fw'); const ctx = canvas.getContext('2d'); let W, H; function resize() { W = canvas.width = window.innerWidth; H = canvas.height = window.innerHeight; } resize(); window.addEventListener('resize', resize); const particles = []; const colors = ['#ff4d6d', '#ffd166', '#06d6a0', '#4cc9f0', '#f72585']; function explode(x, y) { const color = colors[Math.floor(Math.random() * colors.length)]; const count = 120; // 每次爆炸的粒子数 for (let i = 0; i < count; i++) { const angle = (Math.PI * 2 * i) / count; // 均匀分布角度 const speed = Math.random() * 3 + 2; particles.push({ x, y, vx: Math.cos(angle) * speed, vy: Math.sin(angle) * speed, life: 1, color }); } } function loop() { // 用半透明黑覆盖,形成拖尾而不是硬清屏 ctx.fillStyle = 'rgba(5, 5, 16, 0.25)'; ctx.fillRect(0, 0, W, H); for (let i = particles.length - 1; i >= 0; i--) { const p = particles[i]; p.x += p.vx; p.y += p.vy; p.vy += 0.04; // 重力 p.vx *= 0.985; // 阻力 p.vy *= 0.985; p.life -= 0.012; if (p.life <= 0) { particles.splice(i, 1); continue; } ctx.globalAlpha = p.life; ctx.fillStyle = p.color; ctx.beginPath(); ctx.arc(p.x, p.y, 2, 0, Math.PI * 2); ctx.fill(); } ctx.globalAlpha = 1; requestAnimationFrame(loop); } // 每 800ms 随机位置炸一朵 setInterval(() => { explode(Math.random() * W, Math.random() * H * 0.6 + H * 0.1); }, 800); loop(); </script> </body> </html>逻辑说明:explode负责在指定坐标生成一批粒子,角度用(Math.PI * 2 * i) / count均匀铺开,保证爆炸是圆形而不是一坨。loop是主循环,用requestAnimationFrame驱动,比setInterval更贴合屏幕刷新率。注意清屏用的是半透明矩形而不是clearRect,这样上一帧的粒子会留下淡淡残影,形成拖尾——这是烟花「好看」的关键,很多人第一次写忘了这步,画面就很生硬。
3.2 四个必调参数:粒子数、重力、阻力、衰减
代码能跑之后,真正决定观感的是这四个值,建议逐个改、逐个看:
| 参数 | 位置 | 作用 | 推荐范围 | 调过头会怎样 |
|---|---|---|---|---|
| 粒子数 count | explode 内 | 爆炸密度 | 80~300 | 超过 500 中低端机掉帧 |
| 重力 | p.vy += 0.04 | 下落速度 | 0.02~0.08 | 太大像下雨,太小像失重 |
| 阻力 | p.vx *= 0.985 | 扩散减速 | 0.97~0.995 | 太接近 1 粒子飞太远 |
| 衰减 | p.life -= 0.012 | 存活时长 | 0.008~0.02 | 太大一闪就没,太小糊成一片 |
我一般先定粒子数,再调重力和衰减,最后微调阻力。顺序反了会来回改,很费时间。另外颜色数组别放太多高饱和色,三到五种就够,多了反而显脏。
3.3 让烟花「像真的」:拖尾、二次爆炸与随机性
基础版跑通后,加三个小改动就能明显提升质感。第一是拖尾,前面已经用半透明清屏实现,把0.25调小到0.1拖尾更长。第二是二次爆炸,在粒子生命衰减到某个阈值时再触发一次小规模explode,模拟礼花的子母效果。第三是随机性,别让每朵烟花都一样——给count、speed、颜色都加随机区间,画面立刻活起来。
// 二次爆炸:粒子快消失时再炸一次小的 if (p.life < 0.3 && !p.hasSplit && Math.random() < 0.02) { p.hasSplit = true; for (let k = 0; k < 20; k++) { const a = Math.random() * Math.PI * 2; const s = Math.random() * 2 + 1; particles.push({ x: p.x, y: p.y, vx: Math.cos(a) * s, vy: Math.sin(a) * s, life: 0.6, color: p.color }); } }hasSplit标记防止同一个粒子反复分裂,Math.random() < 0.02控制分裂概率,太高会瞬间把粒子数翻好几倍导致卡顿。这段加在粒子更新循环里即可。
4. CSS 与 jQuery 两套方案的落地细节:什么时候该放弃 Canvas
不是所有场景都值得上 Canvas。如果你的页面是纯静态的、或者项目里已经有一堆 jQuery 代码,硬塞一套 Canvas 反而增加维护成本。这一章讲清楚另外两条路线怎么落地,以及各自的边界在哪。
4.1 纯 CSS 烟花:用变量注入方向,零 JS 也能炸
CSS 方案的核心是「一套动画 + 多个元素 + 内联变量」。先定义好扩散动画,再用 JS(或干脆手写)生成一批.spark元素,每个元素通过style注入不同的--dx、--dy和animation-delay。
// 生成 CSS 烟花粒子,注入随机方向 function cssFirework(container, x, y, count = 60) { for (let i = 0; i < count; i++) { const spark = document.createElement('div'); spark.className = 'spark'; const angle = (Math.PI * 2 * i) / count; const dist = Math.random() * 120 + 60; // 扩散距离 spark.style.left = x + 'px'; spark.style.top = y + 'px'; spark.style.setProperty('--dx', Math.cos(angle) * dist + 'px'); spark.style.setProperty('--dy', Math.sin(angle) * dist + 'px'); spark.style.animationDelay = Math.random() * 0.2 + 's'; spark.style.background = ['#ff4d6d', '#ffd166', '#4cc9f0'][i % 3]; container.appendChild(spark); // 动画结束后移除节点,避免 DOM 无限增长 spark.addEventListener('animationend', () => spark.remove()); } }这里最关键的一行是spark.addEventListener('animationend', () => spark.remove())。CSS 烟花最容易翻车的地方就是节点不回收,跑几分钟页面里堆几千个 div,直接卡死。animationend事件保证每个粒子动画播完就删,内存不会涨。dist控制扩散半径,animationDelay加一点随机让粒子不是整齐划一地飞出去。
CSS 方案的适用边界很明确:粒子总数别超过 100,页面别频繁触发,否则布局压力会盖过它的「零 JS」优势。
4.2 jQuery 版烟花:老项目里的兼容写法与性能红线
如果项目里已经有 jQuery,用animate()写烟花是最省事的。核心是创建 div 后用animate改left、top、opacity,动画结束回调里remove()。
// jQuery 版:创建粒子并动画扩散 function jqFirework($container, x, y, count = 40) { for (let i = 0; i < count; i++) { const angle = (Math.PI * 2 * i) / count; const dist = Math.random() * 100 + 50; $('<div class="jq-spark"></div>') .css({ left: x, top: y, background: '#ffd166' }) .appendTo($container) .animate( { left: x + Math.cos(angle) * dist, top: y + Math.sin(angle) * dist, opacity: 0 }, 1000, function () { $(this).remove(); } // 动画完必须删 ); } }性能红线就一条:count别超过 50,触发频率别高于每秒一次。jQuery 的animate底层还是改样式,节点一多就卡。如果你的老项目里烟花要满屏放,我的建议是别硬扛,直接换成 Canvas 方案,jQuery 只负责触发。
4.3 三套方案横向对比:一张表决定用哪个
| 维度 | Canvas 粒子 | 纯 CSS 动画 | jQuery DOM |
|---|---|---|---|
| 粒子上限 | 300~1000 | 60~100 | 30~50 |
| JS 依赖 | 需要 | 可零 JS | 需要 jQuery |
| 上手难度 | 中 | 低 | 低 |
| 移动端表现 | 好 | 好 | 差 |
| 适合场景 | 满屏烟花、大屏 | 静态页点缀 | 老项目兼容 |
| 定制灵活度 | 高 | 中 | 中 |
看完这张表,选型基本不用纠结了。新项目优先 Canvas,静态页用 CSS,老项目实在动不了再上 jQuery。
5. 烟花代码避坑清单:五个让新手反复翻车的点
这一章是我自己踩过、也见过别人反复踩的坑,按「现象 → 原因 → 解决」写。每一条都对应真实会遇到的报错或卡顿,照着排查能省不少时间。
5.1 现象:页面越跑越卡,几分钟后直接卡死
原因:粒子数组或 DOM 节点没有回收。Canvas 版常见于particles数组只 push 不 splice;CSS/jQuery 版常见于动画结束后没remove()节点。
解决:Canvas 版在更新循环里判断life <= 0就splice掉;DOM 版一定要绑animationend或animate回调做remove()。可以在控制台打印particles.length或document.querySelectorAll('.spark').length观察是否持续增长。
5.2 现象:烟花只在左上角炸,位置全错
原因:Canvas 的坐标和鼠标/随机位置没对齐,常见于 canvas 尺寸和 CSS 尺寸不一致,或者没处理devicePixelRatio。
解决:确保canvas.width等于window.innerWidth,并且 CSS 里canvas { display: block }去掉默认间隙。高分屏想更清晰,可以按devicePixelRatio放大画布再ctx.scale,但新手先别碰,容易把坐标算乱。
5.3 现象:动画一顿一顿的,像幻灯片
原因:用了setInterval驱动动画,或者每帧做了大量 DOM 操作。
解决:动画循环统一用requestAnimationFrame,它跟着屏幕刷新率走。DOM 方案如果非要用,把粒子数砍到 30 以内,并且避免在循环里读offsetWidth这类会触发重排的属性。
5.4 现象:手机上烟花糊成一团,或者干脆不显示
原因:没做响应式,canvas 尺寸写死;或者移动端对requestAnimationFrame的节流导致粒子更新变慢。
解决:监听resize重设 canvas 尺寸;粒子数在移动端按屏幕宽度动态调整,比如count = window.innerWidth < 768 ? 80 : 200。另外移动端别用box-shadow做发光,性能开销很大,用globalAlpha或颜色本身控制亮度更稳。
5.5 现象:复制了代码但浏览器控制台报错,烟花不出现
原因:多半是<canvas>的 id 和 JS 里getElementById对不上,或者脚本放在了<head>里、DOM 还没加载就执行。
解决:脚本放到</body>前,或者用DOMContentLoaded包一层。检查 id 拼写,getContext('2d')的引号别写错。这类错误控制台会直接指出行号,照着改就行。
注意:排查烟花问题时,先看控制台有没有报错,再看粒子数是否异常增长,最后才怀疑性能。顺序反了会浪费很多时间。
6. 把 11 套代码用出价值:二次开发与效果验证的几个技巧
合集拿到手,别只是双击看看效果就完了。真正让它产生价值的方式是「拆一套、改一套、合一套」。我一般会挑 Canvas 那套当底子,把 CSS 方案的配色和 jQuery 方案的触发逻辑揉进去,做成一个能配置的烟花组件。
具体做法是抽出一个配置对象,把粒子数、颜色、重力、触发间隔都变成参数:
const config = { count: 150, // 粒子数 colors: ['#ff4d6d', '#ffd166', '#4cc9f0'], gravity: 0.04, // 重力 decay: 0.012, // 衰减 interval: 800, // 触发间隔 ms maxParticles: 1200 // 粒子上限,超过就不再生成 };maxParticles是我后来加的最有用的一个参数。它给粒子总数设了个天花板,不管触发多频繁,超过就不再生成新粒子,从根上避免卡死。验证效果时,我习惯打开 Chrome 的 Performance 面板录 10 秒,看帧率是否稳定在 55 以上;如果掉到 30 以下,先把count砍半再看。
另一个技巧是给烟花加「开关」。很多活动页只需要在特定时间点放几秒,没必要一直跑。用一个isRunning标志控制requestAnimationFrame是否继续,页面切到后台时用document.hidden暂停,能省不少电。这些细节合集里的代码不一定都有,但你自己加上去,这套烟花就从「能看」变成「能用」。
最后说个我自己的习惯:每次改完参数,我都会在手机和一台老笔记本上各跑一遍。桌面浏览器流畅不代表移动端流畅,很多翻车都是在真机上才暴露的。希望帮到你。
本文还有配套的精品资源,点击获取