技术
Canvas 2D 粒子效果实现笔记
2025.04.15 5 min

Canvas 2D 粒子效果实现笔记
在构建这个博客的首页背景时,我选择使用 Canvas 2D 而非 Three.js。原因有三:
- 体积更小 — 无需加载 500KB+ 的 3D 引擎
- SSR 友好 — Canvas 2D 在服务端渲染时不会出问题
- 维护简单 — 代码量更少,更容易理解
核心原理
interface Particle {
x: number;
y: number;
vx: number;
vy: number;
}
function updateParticles(particles: Particle[], mouse: { x: number; y: number }) {
for (const p of particles) {
// 基础移动
p.x += p.vx;
p.y += p.vy;
// 鼠标排斥
const dx = p.x - mouse.x;
const dy = p.y - mouse.y;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 100 && dist > 0) {
const force = (100 - dist) * 0.001;
p.vx += (dx / dist) * force;
p.vy += (dy / dist) * force;
}
// 边界反弹
if (p.x < 0 || p.x > canvas.width) p.vx *= -1;
if (p.y < 0 || p.y > canvas.height) p.vy *= -1;
}
} 连线绘制
只有当粒子间距小于阈值时才绘制连线,连线透明度与距离成反比:
const CONNECTION_DISTANCE = 120;
for (let i = 0; i < particles.length; i++) {
for (let j = i + 1; j < particles.length; j++) {
const dx = particles[i].x - particles[j].x;
const dy = particles[i].y - particles[j].y;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < CONNECTION_DISTANCE) {
const alpha = 1 - dist / CONNECTION_DISTANCE;
ctx.strokeStyle = `rgba(240, 192, 64, ${alpha * 0.3})`;
ctx.beginPath();
ctx.moveTo(particles[i].x, particles[i].y);
ctx.lineTo(particles[j].x, particles[j].y);
ctx.stroke();
}
}
} 性能优化
- 粒子数量控制在 100 个以内
- 使用
requestAnimationFrame而非setInterval - 组件卸载时清理动画帧
- 使用
devicePixelRatio适配高分屏
实际效果非常流畅,CPU 占用也很低。