Tips & Tricks

Canvas/WebGL Optimization:Claude Code 实战指南

了解canvas/webgl optimization:Claude Code 实战. 包含实用技巧和代码示例。

Canvas/WebGL开发の魅力と課題

Canvasはゲーム、数据可視化、图片編集、インタラクティブアートなど幅広い用途に使えます。一方でAPIが低レベルなため、コード量が多くなりがちです。借助 Claude Code,描画ロジックやシェーダーコードを快速生成し、开发速度を大幅に向上可以。

Canvas 2Dの基本描画システム

> Canvas 2Dの描画を管理する类を作って。
> 动画ループ、リサイズ支持、FPS显示を含めて。
class CanvasApp {
  private canvas: HTMLCanvasElement;
  private ctx: CanvasRenderingContext2D;
  private animationId: number = 0;
  private lastTime: number = 0;
  private fps: number = 0;
  private frameCount: number = 0;
  private fpsTime: number = 0;

  constructor(container: HTMLElement) {
    this.canvas = document.createElement('canvas');
    this.ctx = this.canvas.getContext('2d')!;
    container.appendChild(this.canvas);
    this.resize();
    window.addEventListener('resize', () => this.resize());
  }

  private resize() {
    const dpr = window.devicePixelRatio || 1;
    const rect = this.canvas.parentElement!.getBoundingClientRect();
    this.canvas.width = rect.width * dpr;
    this.canvas.height = rect.height * dpr;
    this.canvas.style.width = `${rect.width}px`;
    this.canvas.style.height = `${rect.height}px`;
    this.ctx.scale(dpr, dpr);
  }

  start(renderFn: (ctx: CanvasRenderingContext2D, dt: number) => void) {
    const loop = (time: number) => {
      const dt = (time - this.lastTime) / 1000;
      this.lastTime = time;

      // FPS計算
      this.frameCount++;
      if (time - this.fpsTime >= 1000) {
        this.fps = this.frameCount;
        this.frameCount = 0;
        this.fpsTime = time;
      }

      const { width, height } = this.canvas.getBoundingClientRect();
      this.ctx.clearRect(0, 0, width, height);

      renderFn(this.ctx, dt);

      // FPS显示
      this.ctx.fillStyle = '#0f0';
      this.ctx.font = '12px monospace';
      this.ctx.fillText(`FPS: ${this.fps}`, 10, 20);

      this.animationId = requestAnimationFrame(loop);
    };

    this.animationId = requestAnimationFrame(loop);
  }

  stop() {
    cancelAnimationFrame(this.animationId);
  }
}

パーティクルシステム

> パーティクルエフェクト实现。マウスに追従するパーティクルを作って。
interface Particle {
  x: number; y: number;
  vx: number; vy: number;
  life: number; maxLife: number;
  size: number;
  color: string;
}

class ParticleSystem {
  private particles: Particle[] = [];
  private maxParticles = 500;

  emit(x: number, y: number, count: number = 5) {
    for (let i = 0; i < count; i++) {
      if (this.particles.length >= this.maxParticles) break;

      const angle = Math.random() * Math.PI * 2;
      const speed = Math.random() * 3 + 1;
      const hue = Math.random() * 60 + 200; // 青〜紫

      this.particles.push({
        x, y,
        vx: Math.cos(angle) * speed,
        vy: Math.sin(angle) * speed,
        life: 1,
        maxLife: Math.random() * 1 + 0.5,
        size: Math.random() * 4 + 2,
        color: `hsl(${hue}, 80%, 60%)`,
      });
    }
  }

  update(dt: number) {
    this.particles = this.particles.filter((p) => {
      p.x += p.vx;
      p.y += p.vy;
      p.vy += 0.5 * dt; // 重力
      p.life -= dt / p.maxLife;
      return p.life > 0;
    });
  }

  draw(ctx: CanvasRenderingContext2D) {
    this.particles.forEach((p) => {
      ctx.globalAlpha = p.life;
      ctx.fillStyle = p.color;
      ctx.beginPath();
      ctx.arc(p.x, p.y, p.size * p.life, 0, Math.PI * 2);
      ctx.fill();
    });
    ctx.globalAlpha = 1;
  }
}

WebGLシェーダー

> WebGLで簡単なグラデーションシェーダーを作って。
function createShaderProgram(gl: WebGLRenderingContext) {
  const vertexShaderSource = `
    attribute vec2 a_position;
    varying vec2 v_uv;

    void main() {
      v_uv = a_position * 0.5 + 0.5;
      gl_Position = vec4(a_position, 0.0, 1.0);
    }
  `;

  const fragmentShaderSource = `
    precision mediump float;
    varying vec2 v_uv;
    uniform float u_time;

    void main() {
      vec3 color1 = vec3(0.1, 0.3, 0.8);
      vec3 color2 = vec3(0.8, 0.2, 0.5);
      float t = sin(v_uv.x * 3.0 + u_time) * 0.5 + 0.5;
      vec3 color = mix(color1, color2, t * v_uv.y);
      gl_FragColor = vec4(color, 1.0);
    }
  `;

  const vertexShader = compileShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
  const fragmentShader = compileShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);

  const program = gl.createProgram()!;
  gl.attachShader(program, vertexShader);
  gl.attachShader(program, fragmentShader);
  gl.linkProgram(program);

  return program;
}

function compileShader(gl: WebGLRenderingContext, type: number, source: string) {
  const shader = gl.createShader(type)!;
  gl.shaderSource(shader, source);
  gl.compileShader(shader);
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    throw new Error(gl.getShaderInfoLog(shader) || 'シェーダーコンパイルエラー');
  }
  return shader;
}

Reactとの集成

function CanvasComponent() {
  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!containerRef.current) return;

    const app = new CanvasApp(containerRef.current);
    const particles = new ParticleSystem();

    containerRef.current.addEventListener('mousemove', (e) => {
      const rect = containerRef.current!.getBoundingClientRect();
      particles.emit(e.clientX - rect.left, e.clientY - rect.top);
    });

    app.start((ctx, dt) => {
      particles.update(dt);
      particles.draw(ctx);
    });

    return () => app.stop();
  }, []);

  return <div ref={containerRef} className="w-full h-96 bg-gray-900 rounded-lg" />;
}

总结

借助 Claude Code,Canvas 2Dの描画システムからパーティクルエフェクト、WebGLシェーダーまで高效地开发可以。音频との联动はWeb Audio APIを、数据の可視化は数据可視化の文章

Canvas API的详细信息请参阅MDN Web Docs - Canvas API、WebGL相关内容请参阅WebGL Fundamentals

#Claude Code #Canvas #WebGL #グラフィックス #TypeScript