彩带庆祝特效(Canvas 彩带粒子,发射/爆发/持续撒花三模式)
彩带庆祝特效:自研 Canvas 粒子系统,支持单发彩带、全屏爆发、持续撒花三种模式,数量/扩散角度/重力实时可调,无第三方依赖,点击按钮即可触发。
效果演示(真实可交互)
完整代码
<canvas id="confetti" style="width:100%;height:320px;"></canvas>
<button onclick="fire()">🎉 发射彩带</button>
const canvas = document.getElementById('confetti');
const ctx = canvas.getContext('2d');
let W, H, pieces = [], rafId = null;
const PALETTE = ['#ff5f6d', '#ffc145', '#4be3ac', '#6c8cff', '#ff8ecf', '#ffd166', '#a96cff'];
function resize() {
const r = canvas.getBoundingClientRect();
const dpr = window.devicePixelRatio || 1;
W = canvas.width = r.width * dpr;
H = canvas.height = r.height * dpr;
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
}
function spawn(x, y, count, spread) {
for (let i = 0; i < count; i++) {
const angle = -Math.PI / 2 + (Math.random() - .5) * (spread * Math.PI / 180);
const speed = Math.random() * 9 + 5;
pieces.push({
x, y,
vx: Math.cos(angle) * speed,
vy: Math.sin(angle) * speed,
size: Math.random() * 7 + 4,
rot: Math.random() * Math.PI * 2,
vrot: (Math.random() - .5) * .35,
color: PALETTE[(Math.random() * PALETTE.length) | 0],
shape: Math.random() > .5 ? 'rect' : 'circle',
wobble: Math.random() * Math.PI * 2,
age: 0,
maxAge: Math.random() * 160 + 220,
});
}
loop();
}
function fire() { spawn(W / 2, H * .92, 150, 75); }
function loop() {
if (rafId) return;
const step = () => {
ctx.clearRect(0, 0, W, H);
for (let i = pieces.length - 1; i >= 0; i--) {
const p = pieces[i];
p.age++;
p.vy += 0.18;
p.vx *= 0.985;
p.x += p.vx + Math.sin(p.age * 0.05 + p.wobble) * 0.8;
p.y += p.vy;
p.rot += p.vrot;
const life = 1 - p.age / p.maxAge;
if (life <= 0 || (p.y > H + 40 && p.vy > 0)) { pieces.splice(i, 1); continue; }
ctx.save();
ctx.globalAlpha = Math.max(life, 0);
ctx.translate(p.x, p.y);
ctx.rotate(p.rot);
ctx.fillStyle = p.color;
if (p.shape === 'rect') ctx.fillRect(-p.size / 2, -p.size / 3, p.size, p.size * .66);
else { ctx.beginPath(); ctx.arc(0, 0, p.size / 2, 0, Math.PI * 2); ctx.fill(); }
ctx.restore();
}
rafId = pieces.length ? requestAnimationFrame(step) : null;
};
rafId = requestAnimationFrame(step);
}
resize(); fire();
window.addEventListener('resize', resize);
核心机制:粒子以一定初速度从原点喷射,受重力持续下落,带旋转、摆动和生命周期淡出;粒子全部落地后自动停止动画循环,零粒子零开销。
参数说明
| 参数 | 位置 | 说明 |
|---|---|---|
| 数量 | spawn(x, y, count, spread) 的 count | 单次发射的粒子数,30-400 |
| 扩散角度 | spread(度) | 喷射的扇形开角,75° 为经典效果,越大越散 |
| 重力 | p.vy += 0.18 | 每帧重力增量,越大下落越快 |
| 粒子大小 | Math.random()*7+4 | 矩形宽/圆形直径范围 |
| 生命周期 | p.maxAge | 220-380 帧,决定彩带飘多久 |
兼容性与性能
- 纯原生 Canvas 2D,零依赖、压缩后约 1.5KB,远小于主流彩带库
- 动画循环采用「按需启动」:有粒子才跑
requestAnimationFrame,全部落地即自动停止,常驻页面也不耗电 - 已适配
devicePixelRatio,高分屏清晰;粒子数 ≤400 时现代设备帧率稳定 60fps - 支持所有现代浏览器;iOS Safari 对 Canvas 2D 支持良好,建议移动端单次 ≤200 粒子
常见问题
Q: 和 canvas-confetti 什么关系? 思路(粒子喷射 + 重力 + 旋转)有参考,但代码为粤喵完全自研重写:支持圆形/矩形双形状、摆动轨迹、按需启停等差异化设计,且不引入任何第三方依赖,license 为 self,可放心商用。
Q: 如何做成「按钮点击庆祝」? 在按钮的点击事件里调用 fire() 即可;如需从按钮位置发射,把 W/2, H*.92 替换为按钮的屏幕坐标(getBoundingClientRect() 相对 canvas 换算)。
Q: 想庆祝成功后自动触发,怎么做? 在你自己的业务逻辑(如表单提交成功、订单完成)里调用 fire();配合 setInterval 每 240ms 调用一次即可实现「持续撒花」模式(演示里的勾选框就是这个原理)。
Q: 彩带数量多了会卡吗? 400 粒子是现代设备的舒适上限。超过后建议降低单次数量、改用间隔多次发射,或把粒子的 maxAge 缩短,让旧粒子更快离场。
许可与来源
- 许可证:自研原创 —— 粤喵自研原创,可免费用于个人与商业项目,无需署名。查看来源公示与许可清单
- 来源:粤喵团队原创自研(从效果思路到代码实现均为独立完成,未直接复制第三方代码)
- 首次发布:2026-09-01 · 最近更新:2026-09-01
- 修改日志:2026-09-01 首次发布;2026-09-13 补充许可证与来源标注、复制按钮说明与法务页入口
- 纠错:联系页面 / min@cactus.asia,2 个工作日内核实处理
常见问题
这个组件可以商用吗?
可以。本组件为粤喵自研原创,可免费用于个人和商业项目,无需署名。
浏览器兼容性如何?
支持所有现代浏览器(Chrome / Firefox / Safari / Edge 最新版本)。旧版 IE 不支持 CSS 动画相关特性。
怎么修改动画参数?
直接调整 CSS 中的 animation-duration(时长)、animation-timing-function(缓动曲线)等属性即可,代码中有注释说明。