·tech

粒子背景特效 — Canvas 高性能渲染优化

Canvas 几何粒子系统的性能优化实战:距离平方比较、离屏预渲染、响应式粒子数、DPR 限制。

粒子背景特效 — Canvas 高性能渲染优化

ARKNEXUS 的几何粒子背景是一个性能敏感的 Canvas 应用。本文记录优化过程。

设计目标

与常见的圆形粒子不同,方舟风格需要几何形状的粒子:

  • 六边形(主)— 占比 40%
  • 菱形 — 占比 25%
  • 三角形 — 占比 20%
  • 短线段 — 占比 15%

核心架构

typescript46 L
interface Particle {
  x: number; y: number;
  vx: number; vy: number;
  type: "hexagon" | "diamond" | "triangle" | "line";
  size: number;
  rotation: number;
  opacity: number;
  color: string;
}

function animate(time: number) {
  const dt = (time - lastTime) / 16.667;
  lastTime = time;

  ctx.clearRect(0, 0, w, h);

  // 1. 更新粒子位置
  for (const p of particles) {
    p.x += p.vx * dt;
    p.y += p.vy * dt;
    // 边界反弹
    if (p.x < 0) p.vx *= -1;
    if (p.x > w) p.vx *= -1;
    if (p.y < 0) p.vy *= -1;
    if (p.y > h) p.vy *= -1;
  }

  // 2. 鼠标交互力
  for (const p of particles) {
    const dx = p.x - mouseX;
    const dy = p.y - mouseY;
    const distSq = dx * dx + dy * dy;
    if (distSq < MOUSE_RADIUS_SQ) {
      const dist = Math.sqrt(distSq);
      const force = (1 - dist / MOUSE_RADIUS) * 1.5;
      p.vx += (dx / dist) * force * 0.05 * dt;
      p.vy += (dy / dist) * force * 0.05 * dt;
    }
  }

  // 3. 绘制
  drawParticles(ctx, particles);
  drawConnections(ctx, particles, CONNECTION_DIST_SQ);

  requestAnimationFrame(animate);
}

优化策略

1. 距离平方比较

typescript7 L
// ❌ 慢:每次都要开方
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < CONNECTION_DIST) { ... }

// ✅ 快:比较平方值
const distSq = dx * dx + dy * dy;
if (distSq < CONNECTION_DIST_SQ) { ... }

2. 粒子数响应式

typescript5 L
function getParticleCount(width: number): number {
  if (width < 640) return 30;   // 手机
  if (width < 1024) return 60;  // 平板
  return 120;                     // 桌面
}

3. 离屏 Canvas 预渲染

typescript10 L
// 预渲染六边形到离屏 Canvas
const offscreen = document.createElement("canvas");
offscreen.width = 20; offscreen.height = 20;
const octx = offscreen.getContext("2d")!;
octx.beginPath();
// 绘制六边形路径...
octx.stroke();

// 主循环中直接用 drawImage
ctx.drawImage(offscreen, p.x - 10, p.y - 10);

4. DPR 限制

typescript4 L
const dpr = Math.min(window.devicePixelRatio || 1, 2);
canvas.width = width * dpr;
canvas.height = height * dpr;
ctx.scale(dpr, dpr);

性能基准

设备粒子数FPS优化前 FPS
iPhone 14306045
iPad Air606050
MacBook Pro1206055

完整代码见 ARKNEXUS 源码 hooks/useParticles.ts。