手把手教你制作绚丽的烟花动画
目录导读
- 烟花动画的魅力与工具选择 – 为什么用代码模拟烟花?主流工具有哪些?
- 核心原理:粒子系统与物理模拟 – 烟花“爆炸”背后的数学与算法
- 实战演练1:用Canvas+JavaScript制作基础烟花 – 从零写一个可运行的动画
- 实战演练2:用CSS+Keyframes实现极简烟花 – 无需JS的轻量方案
- 进阶技巧:引入Three.js打造3D烟花 – 让烟花在三维空间绽放
- 常见问题与优化建议 – 性能调优、颜色随机、轨迹曲线
- Q&A问答集锦 – 解决新手最常遇到的5个疑问
烟花动画的魅力与工具选择
烟花是节日庆典的视觉符号,而用代码模拟烟花动画,既是对物理现象的艺术再现,也是前端开发、动画设计领域的热门练习项目,通过工具制作烟花动画,你不仅能掌握粒子系统、缓动函数等核心编程概念,还能直接产出炫酷的可交互页面。

主流工具对比: | 工具/技术 | 适用场景 | 学习曲线 | 性能 | |-----------|----------|----------|------| | Canvas + JavaScript | 2D粒子特效,灵活自定义 | 中等 | 高(硬件加速) | | CSS3 Keyframes | 简单粒子,无需脚本 | 低 | 高(GPU加速) | | Three.js / WebGL | 3D立体烟花,VR效果 | 高 | 极高(需优化) | | Processing / p5.js | 教育创意,快速原型 | 低 | 一般 |
推荐组合: 新手从“Canvas + JS”起步,掌握原理后尝试Three.js进阶。
核心原理:粒子系统与物理模拟
任何一个烟花动画,本质都是粒子系统的体现,一个完整的烟花由三个阶段组成:
- 发射阶段: 一个粒子(烟花弹)沿抛物线上升。
- 爆炸阶段: 原粒子分裂为数百个小粒子,向不同方向飞散。
- 消散阶段: 粒子受到重力、空气阻力影响,速度衰减并淡出。
关键公式:
- 粒子位置更新:
x += vx * dt,y += vy * dt(dt为时间步长) - 重力影响:
vy += gravity * dt - 透明度衰减:
alpha -= fadeRate * dt
这些物理模拟让动画看起来真实自然。
实战演练1:用Canvas+JavaScript制作基础烟花
步骤1:HTML骨架
<!DOCTYPE html> <html> <head><title>烟花动画</title></head> <body> <canvas id="fireworks" width="800" height="600"></canvas> <script src="fireworks.js"></script> </body> </html>
步骤2:JavaScript核心代码
const canvas = document.getElementById('fireworks');
const ctx = canvas.getContext('2d');
class Particle {
constructor(x, y, color) {
this.x = x;
this.y = y;
this.color = color;
this.vx = (Math.random() - 0.5) * 6;
this.vy = (Math.random() - 0.5) * 6;
this.life = 1; // 透明度
this.decay = Math.random() * 0.02 + 0.005;
}
update() {
this.x += this.vx;
this.y += this.vy;
this.vy += 0.01; // 重力
this.life -= this.decay;
}
draw() {
ctx.globalAlpha = Math.max(this.life, 0);
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, 3, 0, Math.PI * 2);
ctx.fill();
}
}
let particles = [];
// 自动生成烟花
setInterval(() => {
for (let i = 0; i < 200; i++) {
const hue = Math.random() * 360;
particles.push(new Particle(
Math.random() * canvas.width,
Math.random() * canvas.height / 2,
`hsl(${hue}, 100%, 60%)`
));
}
}, 2000);
function animate() {
ctx.fillStyle = 'rgba(0,0,0,0.1)'; // 拖尾效果
ctx.fillRect(0, 0, canvas.width, canvas.height);
particles.forEach(p => { p.update(); p.draw(); });
particles = particles.filter(p => p.life > 0);
requestAnimationFrame(animate);
}
animate();
代码要点:
- 使用HSL颜色模型生成随机亮色
- 每2秒生成一个“爆炸群”
- 利用
rgba(0,0,0,0.1)实现拖尾渐隐效果
效果说明: 运行后会出现彩色粒子向四周飞散,并受重力缓慢下落。
实战演练2:用CSS+Keyframes实现极简烟花
如果你想要一个无需JavaScript的轻量方案,纯CSS也能模拟简单的烟花效果。
步骤1:HTML结构
<div class="firework"> <div class="spark" style="--x:50; --y:0; --color:red;"></div> <div class="spark" style="--x:-30; --y:40; --color:gold;"></div> <!-- 重复多个火花 --> </div>
步骤2:CSS动画
.firework { position: relative; width: 100px; height: 100px; }
.spark {
position: absolute;
width: 4px; height: 4px;
border-radius: 50%;
background: var(--color);
animation: burst 1s ease-out forwards;
opacity: 0;
}
@keyframes burst {
0% { opacity: 1; transform: translate(0,0) scale(1); }
100% { opacity: 0; transform: translate(calc(var(--x) * 1px), calc(var(--y) * 1px)) scale(0.2); }
}
优缺点: 实现简单,但粒子数量受限于HTML元素个数,适合少量装饰性烟花。
进阶技巧:引入Three.js打造3D烟花
借助Three.js库,你可以让烟花在3D空间中旋转、缩放,甚至响应用户视角。
安装与初始化:
npm install three
核心3D粒子代码:
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 生成粒子纹理(渐变圆点)
const canvas = document.createElement('canvas');
const ctx2d = canvas.getContext('2d');
canvas.width = 32; canvas.height = 32;
const gradient = ctx2d.createRadialGradient(16,16,0,16,16,16);
gradient.addColorStop(0, 'rgba(255,255,255,1)');
gradient.addColorStop(1, 'rgba(255,255,255,0)');
ctx2d.fillStyle = gradient;
ctx2d.fillRect(0,0,32,32);
const texture = new THREE.CanvasTexture(canvas);
const material = new THREE.PointsMaterial({ map: texture, blending: THREE.AdditiveBlending });
const geometry = new THREE.BufferGeometry();
// 每帧更新粒子位置
function createBurst(x, y, z) {
const positions = [];
const colors = [];
for (let i = 0; i < 500; i++) {
const angle = Math.random() * Math.PI * 2;
const radius = Math.random() * 5 + 2;
positions.push(x + Math.cos(angle) * radius, y + Math.sin(angle) * radius, z + (Math.random()-0.5)*2);
colors.push(Math.random(), Math.random(), Math.random());
}
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
const points = new THREE.Points(geometry, material);
scene.add(points);
// 2秒后移除
setTimeout(() => scene.remove(points), 2000);
}
// 每3秒触发一次爆炸
setInterval(() => createBurst(0, 0, 0), 3000);
特性: 粒子发光且带有3D景深感,支持鼠标拖拽旋转视角。
常见问题与优化建议
-
性能卡顿怎么办?
- 减少粒子数量(单个爆炸控制在200-500个)
- 使用
requestAnimationFrame而非setInterval - 对于旧的Canvas,使用
canvas.getContext('2d', { alpha: false })禁用透明通道
-
如何实现多彩渐变?
- 在粒子初始化时,从渐变色彩数组中随机选取
- 使用HSL颜色模型动态改变色相
-
烟花轨迹如何弯曲?
- 在粒子发射阶段加入水平速度随机偏移
- 或者让初始粒子拥有不同方向的初速度
-
怎样让烟花自动连续爆炸?
- 使用
setInterval随机延迟,或者结合鼠标/触摸事件触发
- 使用
Q&A问答集锦
Q1:我是前端零基础,能学会吗? A:可以,从CSS Keyframes方案开始(仅需HTML+CSS知识),然后学一点JavaScript基础后再挑战Canvas版本,建议先理解“粒子 = 位置 + 速度 + 生命周期”这个核心思想。
Q2:如何让烟花呈现“心形”或“字母”形状?
A:使用预定义的坐标数组,例如心形公式:x = 16 * sin³(t),y = 13*cos(t) - 5*cos(2t) - 2*cos(3t) - cos(4t),生成爆炸粒子的目标位置。
Q3:为什么我的Canvas烟花动画会闪烁?
A:最常见原因是未正确清空画布,确保每帧开头使用ctx.clearRect(0, 0, canvas.width, canvas.height),或者在Canvas上绘制半透明黑色矩形制造拖尾效果。
Q4:Three.js版本可以添加音效吗?
A:可以,使用Audio或Web Audio API,在createBurst函数内播放new Audio('boom.mp3')即可,注意音频文件需要跨域支持。
Q5:移动端兼容性如何?
A:Canvas方案对移动端友好,但需处理devicePixelRatio以保证高清显示,Three.js需注意WebGL支持情况,旧机型可降级为Canvas版本。