1. 项目概述
最近在整理个人作品集时,突然想起去年春节做的一个小项目——用纯HTML+CSS+JS实现网页版烟花特效。这个看似简单的效果,实际上涉及不少前端动画的核心技术点。今天就把这个项目的上半部分实现思路和关键代码分享给大家,特别适合刚入门前端想练手动画效果的朋友。
不同于常见的Canvas实现方案,我这次刻意避开了Canvas API,纯粹用DOM操作+CSS动画来实现。这种方案虽然性能上不如Canvas,但有两个显著优势:一是代码更直观易懂,二是兼容性更好,连IE11都能流畅运行(当然现在可能没人关心IE了)。实际测试在移动端浏览器上也能保持60fps的流畅度,足够应付大多数展示场景。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心设计思路
2.1 技术选型考量
选择纯DOM方案主要基于以下考虑:
- 开发效率:DOM操作比Canvas API更符合前端开发者的直觉思维
- 调试便利:可以直接在浏览器开发者工具中实时修改样式
- 扩展性强:每个烟花粒子都是独立DOM元素,方便添加交互效果
注意:如果项目需要渲染上千个粒子,建议还是改用Canvas或WebGL方案。DOM方案在粒子数超过500时就会出现明显性能下降。
2.2 物理模型简化
真实的烟花物理模型非常复杂,我们做了适当简化:
- 抛物线运动分解为水平匀速和垂直匀加速运动
- 空气阻力简化为速度的线性衰减
- 粒子大小随生命周期逐渐缩小
- 颜色使用HSL模型实现平滑过渡
javascript复制// 粒子运动公式简化示例
function updateParticle() {
vx *= 0.98; // 水平速度衰减
vy += 0.1; // 垂直加速度(重力)
x += vx;
y += vy;
size *= 0.97; // 尺寸衰减
}
3. 关键实现步骤
3.1 基础结构搭建
首先创建基础的HTML结构,需要注意几个关键点:
html复制<!DOCTYPE html>
<html>
<head>
<style>
body {
margin: 0;
overflow: hidden;
background: #000;
}
.particle {
position: absolute;
border-radius: 50%;
pointer-events: none;
transform: translate(-50%, -50%);
}
</style>
</head>
<body>
<script src="fireworks.js"></script>
</body>
</html>
这里特别设置了pointer-events: none,避免大量粒子影响页面交互性能。
3.2 粒子系统实现
粒子系统的核心是一个对象池管理机制:
javascript复制class ParticleSystem {
constructor() {
this.pool = [];
this.activeParticles = [];
}
createParticle(x, y, color) {
let particle = this.pool.pop();
if (!particle) {
particle = document.createElement('div');
particle.className = 'particle';
document.body.appendChild(particle);
}
// 初始化粒子状态
particle.style.backgroundColor = color;
particle.style.width = particle.style.height = '8px';
this.activeParticles.push({
el: particle,
x, y, vx, vy, life
});
}
update() {
for (let i = 0; i < this.activeParticles.length; i++) {
const p = this.activeParticles[i];
// 更新粒子位置和生命周期
if (p.life <= 0) {
this.recycleParticle(p, i);
i--;
}
}
}
}
3.3 烟花发射逻辑
烟花发射分为三个阶段:
- 上升阶段:垂直向上加速
- 爆炸阶段:达到顶点时生成爆炸粒子
- 消散阶段:粒子减速下落
javascript复制function launchFirework(x) {
const rocket = {
x, y: window.innerHeight,
vx: Math.random() * 2 - 1,
vy: -15,
life: 100
};
function animate() {
rocket.vy += 0.2;
rocket.x += rocket.vx;
rocket.y += rocket.vy;
if (rocket.vy >= 0) {
explode(rocket.x, rocket.y);
return;
}
if (rocket.life-- > 0) {
requestAnimationFrame(animate);
}
}
animate();
}
4. 效果优化技巧
4.1 性能优化方案
-
使用transform代替top/left:
现代浏览器对transform有硬件加速,性能更好 -
限制最大粒子数:
当活跃粒子超过阈值时,停止生成新粒子 -
减少重绘区域:
给粒子容器设置will-change属性
css复制.particle {
will-change: transform, opacity;
}
4.2 视觉增强技巧
- 颜色生成算法:
使用HSL色彩空间可以轻松生成协调的颜色组合
javascript复制function randomColor() {
return `hsl(${Math.random() * 360}, 100%, 50%)`;
}
- 粒子拖尾效果:
通过box-shadow给粒子添加光晕
css复制.particle {
box-shadow: 0 0 5px 2px currentColor;
}
- 多重爆炸效果:
主爆炸后0.5秒再触发几次小爆炸
5. 常见问题排查
5.1 粒子闪烁问题
现象:粒子在移动过程中出现闪烁
解决方案:
- 检查是否同时修改了left和transform属性
- 确保所有动画都使用requestAnimationFrame
- 给容器元素添加backface-visibility: hidden
5.2 内存泄漏问题
现象:页面运行一段时间后变卡顿
排查步骤:
- 使用Chrome内存快照工具检查DOM节点数量
- 确认粒子回收逻辑正确执行
- 检查事件监听器是否及时移除
5.3 移动端兼容问题
现象:在iOS上动画不流畅
优化方案:
- 添加-webkit-transform前缀
- 减少同时显示的粒子数量
- 使用touch事件替代mouse事件
6. 完整源码解析
由于篇幅限制,这里展示核心代码结构:
javascript复制class Fireworks {
constructor(options = {}) {
// 初始化配置
this.config = {
maxParticles: 300,
gravity: 0.1,
...options
};
this.setup();
this.bindEvents();
}
setup() {
this.particleSystem = new ParticleSystem();
this.rafId = null;
this.lastTime = 0;
}
bindEvents() {
window.addEventListener('click', (e) => {
this.launchFirework(e.clientX);
});
}
launchFirework(x) {
// 发射逻辑
}
explode(x, y) {
// 爆炸逻辑
}
start() {
this.stop();
this.loop();
}
loop(timestamp) {
// 主动画循环
this.rafId = requestAnimationFrame(this.loop.bind(this));
// 控制帧率
if (!this.lastTime) this.lastTime = timestamp;
const delta = timestamp - this.lastTime;
if (delta < 16) return; // 约60fps
this.update();
this.lastTime = timestamp;
}
update() {
this.particleSystem.update();
}
}
在实际项目中,我还添加了这些增强功能:
- 背景星空效果
- 音效触发
- 屏幕边缘碰撞检测
- 粒子间引力效果
这个项目的完整源码已经上传到GitHub,包含详细注释和多个预设效果。下期我们会继续探讨如何添加3D透视效果和用户交互功能,让烟花能根据鼠标移动产生互动效果。
