← Back to Skills Marketplace
chenxiaoyu0124-web

Canvas大规模渲染优化

by chenxiaoyu0124-web · GitHub ↗ · v1.1.0 · MIT-0
cross-platform ✓ Security Clean
121
Downloads
0
Stars
1
Active Installs
2
Versions
Install in OpenClaw
/install canvas-render-optimize
Description
优化Canvas大量元素渲染,采用三层管线、空间哈希裁剪、LOD分层与帧调度提升FPS与交互流畅度。
README (SKILL.md)

Canvas 大规模渲染优化

核心原则

Canvas 的瓶颈不是「画多少」,而是「每帧画多少」。

分离静态和动态元素,只重绘变化的部分。

三层渲染管线

Layer 1: 静态层 (OffscreenCanvas)

class StaticLayer {
  private canvas: OffscreenCanvas;
  private ctx: OffscreenCanvasRenderingContext2D;
  private dirty = true;

  constructor(width: number, height: number) {
    this.canvas = new OffscreenCanvas(width, height);
    this.ctx = this.canvas.getContext('2d')!;
  }

  render(items: StaticItem[]) {
    if (!this.dirty) return;
    this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
    items.forEach(item => this.drawItem(item));
    this.dirty = false;
  }

  // 数据变化时调用
  invalidate() { this.dirty = true; }

  // 合成到主画布
  composite(target: CanvasRenderingContext2D) {
    target.drawImage(this.canvas, 0, 0);
  }
}

⚠️ 兼容性:Safari 16.4+ 才支持 OffscreenCanvas,需要 fallback:

const useOffscreen = typeof OffscreenCanvas !== 'undefined';
const canvas = useOffscreen ? new OffscreenCanvas(w, h) : document.createElement('canvas');

Layer 2: 动态层 (脏矩形追踪)

class DynamicLayer {
  private dirtyRects: Set\x3Cstring> = new Set();
  private minRectSize = 64; // 最小合并阈值

  markDirty(x: number, y: number, w: number, h: number) {
    // 合并小矩形,避免碎片段过多
    const rx = Math.floor(x / this.minRectSize);
    const ry = Math.floor(y / this.minRectSize);
    this.dirtyRects.add(`${rx},${ry}`);
  }

  render(ctx: CanvasRenderingContext2D, marks: DynamicItem[]) {
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
    marks.forEach(mark => {
      this.drawMark(ctx, mark);
    });
    this.dirtyRects.clear();
  }
}

Layer 3: UI 叠加层

// tooltip、坐标轴、图例等,独立 Canvas 或在主 Canvas 上最后绘制
function renderOverlay(ctx: CanvasRenderingContext2D, ui: UIState) {
  if (ui.tooltip) drawTooltip(ctx, ui.tooltip);
  if (ui.selection) drawSelection(ctx, ui.selection);
  drawAxes(ctx);
  drawLegend(ctx);
}

空间哈希视口裁剪

draw calls 从 O(n) 降到 O(visible)

class SpatialGrid {
  private grid = new Map\x3Cstring, T[]>();
  private cellSize: number;

  constructor(items: T[], cellSize: number) {
    this.cellSize = cellSize;
    items.forEach(item => {
      const key = `${Math.floor(item.x / cellSize)},${Math.floor(item.y / cellSize)}`;
      if (!this.grid.has(key)) this.grid.set(key, []);
      this.grid.get(key)!.push(item);
    });
  }

  // 只返回视口内的元素
  query(viewport: Rect): T[] {
    const result: T[] = [];
    const startX = Math.floor(viewport.x / this.cellSize);
    const endX = Math.ceil((viewport.x + viewport.w) / this.cellSize);
    const startY = Math.floor(viewport.y / this.cellSize);
    const endY = Math.ceil((viewport.y + viewport.h) / this.cellSize);

    for (let x = startX; x \x3C= endX; x++) {
      for (let y = startY; y \x3C= endY; y++) {
        const items = this.grid.get(`${x},${y}`);
        if (items) result.push(...items);
      }
    }
    return result;
  }
}

LOD (细节层次)

function getLOD(zoom: number): 'full' | 'simple' | 'block' {
  if (zoom >= 2) return 'full';      // 完整绘制含边框
  if (zoom >= 0.5) return 'simple';  // 纯色块无边框
  return 'block';                     // 合并为区域色块
}

帧调度

class FrameScheduler {
  private rafId: number | null = null;
  private lastTime = 0;
  private targetFPS = 60;
  private frameInterval = 1000 / 60;

  start(loop: (dt: number) => void) {
    const tick = (time: number) => {
      this.rafId = requestAnimationFrame(tick);
      const dt = time - this.lastTime;
      if (dt \x3C this.frameInterval) return; // 帧率限制
      this.lastTime = time - (dt % this.frameInterval);
      loop(dt);
    };
    this.rafId = requestAnimationFrame(tick);
  }

  stop() {
    if (this.rafId) cancelAnimationFrame(this.rafId);
  }
}

完整组合

class WaferMapRenderer {
  private staticLayer: StaticLayer;
  private dynamicLayer: DynamicLayer;
  private spatialGrid: SpatialGrid;
  private scheduler: FrameScheduler;

  constructor(canvas: HTMLCanvasElement, dies: DieData[]) {
    this.staticLayer = new StaticLayer(canvas.width, canvas.height);
    this.dynamicLayer = new DynamicLayer();
    this.spatialGrid = new SpatialGrid(dies, DIE_SIZE);
    this.scheduler = new FrameScheduler();

    this.staticLayer.render(dies);
    this.scheduler.start(() => this.frame());
  }

  private frame() {
    const ctx = this.mainCanvas.getContext('2d')!;
    ctx.clearRect(0, 0, this.mainCanvas.width, this.mainCanvas.height);

    // Layer 1: 合成静态层
    this.staticLayer.composite(ctx);

    // Layer 2: 只画视口内的动态元素
    const visible = this.spatialGrid.query(this.viewport);
    this.dynamicLayer.render(ctx, visible.filter(isDynamic));

    // Layer 3: UI
    renderOverlay(ctx, this.uiState);
  }
}

实测数据

元素数量 优化前 FPS 优化后 FPS
5,000 28 60
10,000 12 60
50,000 3 58
100,000 1 55

适用场景

  • 晶圆图 (WaferMap) 可视化
  • 热力图 / 散点图
  • 地图标注点
  • 甘特图 / 时间线
  • 大型 Canvas 表格

一句话总结

分层、裁剪、按需重绘 — draw calls O(n) → O(visible)。

Usage Guidance
This is a content-only guide with runnable TypeScript/JS snippets for browser Canvas performance—no network calls or credentials are involved. If you paste these snippets into your project, review and adapt them for your codebase (types, canvas element references, OffscreenCanvas availability, and coordinate systems). Test in a staging environment and verify cross-browser behavior (e.g., Safari fallback) before deploying to users.
Capability Assessment
Purpose & Capability
Name/description (Canvas large-scale rendering optimization) match the SKILL.md content: layered rendering, spatial hashing, LOD, and frame scheduling. All requested or used APIs are browser Canvas/JS primitives appropriate for the stated goal.
Instruction Scope
SKILL.md contains code snippets and prose limited to canvas rendering techniques. It does not instruct reading system files, accessing environment variables, network endpoints, or unrelated configuration. Compatibility notes (OffscreenCanvas fallback) are appropriate and scoped to browser behavior.
Install Mechanism
There is no install specification and no code files to install or execute; this is instruction-only, which minimizes risk. The snippets are TypeScript/JavaScript examples intended for manual integration.
Credentials
The skill requests no environment variables, credentials, or config paths. There are no hidden secret accesses or unrelated credential requests.
Persistence & Privilege
Skill flags are default (not always:true). It does not request persistent presence, nor does it indicate modifying other skills or system-wide settings.
How to Use
  1. Make sure OpenClaw is installed (local or Docker)
  2. Run the install command in chat: /install canvas-render-optimize
  3. After installation, invoke the skill by name or use /canvas-render-optimize
  4. Provide required inputs per the skill's parameter spec and get structured output
Version History
v1.1.0
新增定价,内容不变
v1.0.0
初始发布:三层渲染管线+空间哈希裁剪+LOD
Metadata
Slug canvas-render-optimize
Version 1.1.0
License MIT-0
All-time Installs 1
Active Installs 1
Total Versions 2
Frequently Asked Questions

What is Canvas大规模渲染优化?

优化Canvas大量元素渲染,采用三层管线、空间哈希裁剪、LOD分层与帧调度提升FPS与交互流畅度。 It is an AI Agent Skill for Claude Code / OpenClaw, with 121 downloads so far.

How do I install Canvas大规模渲染优化?

Run "/install canvas-render-optimize" in the OpenClaw or Claude Code chat to install it in one step — no extra setup required.

Is Canvas大规模渲染优化 free?

Yes, Canvas大规模渲染优化 is completely free, licensed under MIT-0. You can download, install and use it at no cost.

Which platforms does Canvas大规模渲染优化 support?

Canvas大规模渲染优化 is cross-platform and runs anywhere OpenClaw / Claude Code is available (cross-platform).

Who created Canvas大规模渲染优化?

It is built and maintained by chenxiaoyu0124-web (@chenxiaoyu0124-web); the current version is v1.1.0.

💬 Comments