彩带庆祝特效(Canvas 彩带粒子,发射/爆发/持续撒花三模式)

CANVASJS 许可:自研原创

彩带庆祝特效:自研 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.maxAge220-380 帧,决定彩带飘多久

兼容性与性能

常见问题

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(缓动曲线)等属性即可,代码中有注释说明。

✍️ 作者与审核