在网页里做烟花效果,常见做法有两种:一种是使用canvas不断重绘粒子,另一种是用DOM节点配合CSS3动画。后者虽然节点数量一多会有性能瓶颈,但代码直观、容易理解,非常适合做教学示例或者并发量不高的氛围动画。下面我们用原生JavaScript创建烟花粒子,用CSS3的keyframes控制绽放与消失。

一、整体实现思路
烟花动画可以拆成两个阶段。第一个阶段是烟花从地面升起到空中爆炸点,我们称之为“上升阶段”;第二个阶段是爆炸后产生大量粒子向四周散开并渐隐,我们称之为“爆炸阶段”。如果用纯CSS写,很难在运行时动态决定爆炸位置和粒子数量,因此我们用JS在运行时生成节点并写入随机参数。
具体来说,点击页面任意位置时,JS记录鼠标坐标作为爆炸中心,然后创建几十个div作为粒子。每个粒子通过CSS变量接收随机角度和距离,再利用transform做位移,用opacity做透明度衰减。这样不需要在JS里写复杂的运动方程,动画交给浏览器合成层处理,更加流畅。
二、HTML结构
页面本身只需要一个容器用来装烟花粒子,不需要提前写死任何烟花节点。所有粒子都由JS动态插入,避免HTML冗余。
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>CSS3+JS烟花</title> <link rel="stylesheet" href="style.css"> </head> <body> <div id="sky"></div> <script src="firework.js"></script> </body> </html>
上面的<div id="sky">就是我们的夜空容器。后续所有烟花粒子都会作为它的子元素存在。保持结构简洁,有助于后面用JS批量操作。
三、CSS3样式与动画
粒子的外观用CSS定义,包括绝对定位、圆形外观和发光效果。真正的“绽放”动作放在keyframes里,通过读取JS写入的CSS变量来计算最终位移。这样同一套动画规则可以服务无数随机粒子。
#sky {
position: fixed;
inset: 0;
background: #000;
overflow: hidden;
}
.particle {
position: absolute;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--color);
box-shadow: 0 0 8px var(--color);
opacity: 1;
animation: burst var(--duration) ease-out forwards;
}
@keyframes burst {
from {
transform: translate(0, 0) scale(1);
opacity: 1;
}
to {
transform: translate(var(--tx), var(--ty)) scale(0.3);
opacity: 0;
}
}
这里用到了CSS自定义属性,比如--tx和--ty表示粒子横向和纵向的位移量,--duration控制动画时长,--color决定粒子颜色。由于animation使用了forwards,动画结束后会停留在最后一帧,也就是透明度为0的状态,方便我们之后统一清理。
需要注意,如果粒子数量很大,建议把box-shadow的模糊半径调小,因为阴影是渲染开销较高的属性。在低配设备上可以仅保留纯色圆点,用数量弥补观感。
四、JavaScript控制逻辑
JS负责三件事:监听用户点击、计算随机粒子参数、创建节点并监听动画结束以回收。下面给出完整示例,其中包含角度均分加随机抖动、速度随机、颜色随机等细节。
const sky = document.getElementById('sky');
function createFirework(x, y) {
const count = 60;
const colors = ['#ff5252', '#ffd740', '#69f0ae', '#40c4ff', '#e040fb'];
for (let i = 0; i < count; i++) {
const p = document.createElement('div');
p.className = 'particle';
const angle = (Math.PI * 2 * i) / count + (Math.random() - 0.5) * 0.3;
const distance = 80 + Math.random() * 70;
const tx = Math.cos(angle) * distance;
const ty = Math.sin(angle) * distance;
p.style.left = x + 'px';
p.style.top = y + 'px';
p.style.setProperty('--tx', tx + 'px');
p.style.setProperty('--ty', ty + 'px');
p.style.setProperty('--duration', 0.8 + Math.random() * 0.6 + 's');
p.style.setProperty('--color', colors[Math.floor(Math.random() * colors.length)]);
p.addEventListener('animationend', () => p.remove());
sky.appendChild(p);
}
}
sky.addEventListener('click', (e) => {
createFirework(e.clientX, e.clientY);
});
在上面的代码中,我们把圆周均分成60份,再给每个角度加一点随机抖动,避免粒子排成死板的圆圈。distance控制爆炸半径,用随机数让烟花有粗细不均的尾巴。animationend事件触发后直接remove节点,能有效防止DOM节点无限累积导致页面卡顿。
如果你希望烟花自动燃放而不依赖点击,可以用setInterval随机生成坐标来调用createFirework。但在生产环境中要限制最大并发粒子数,比如同时不超过三团烟花,否则CSS动画过多会占用主线程合成资源。
五、性能与优化建议
这种DOM+CSS3方案在粒子总数低于三四百时表现良好。若想进一步提升性能,可以开启GPU加速,确保transform和opacity动画运行在合成层。我们已经使用了transform和opacity,这两个属性本身就不会触发重排重绘,属于浏览器优化最好的动画属性。
另一个常见优化是复用节点:提前创建对象池,爆炸时从池里取节点,结束后再放回。这样减少createElement和remove的频繁操作。对于教学和小场景,直接增删节点代码更清晰;对于长时段挂机动画,对象池更有价值。你也可以把粒子改成canvas绘制,用一支requestAnimationFrame循环统一渲染,那样可以轻松支撑上千粒子。
六、小结
用CSS3配合JS实现烟花,核心是把“运动计算”交给JS,把“运动表现”交给CSS。这种分工让代码易读、调试方便。掌握CSS变量与keyframes的结合,你还能做出雪花、流星、礼花筒等多种特效。建议先从点击爆炸练起,再逐步加入上升轨迹和声音,就能做出完整的烟花秀。
CSS3动画JavaScriptcanvas烟花修改时间:2026-08-09 17:24:49