canvas-design
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCanvas Design
Canvas 设计
Overview
概述
Create performant, accessible, and visually compelling graphics using HTML Canvas 2D, SVG, and data visualization libraries. This skill covers everything from low-level pixel manipulation to high-level chart composition with D3.js, including generative art, interactive graphics, and animation.
使用HTML Canvas 2D、SVG和数据可视化库创建高性能、可访问且视觉效果出色的图形。本技能涵盖从底层像素操作到使用D3.js实现高阶图表组合的所有内容,包括生成艺术、交互式图形和动画。
Phase 1: Requirements Analysis
阶段1:需求分析
- Determine output type (static, animated, interactive, data-driven)
- Choose rendering technology (Canvas 2D, SVG, WebGL, hybrid)
- Identify data sources and update frequency
- Define accessibility requirements for the visualization
- Set performance budget (frame rate, element count)
STOP — Present technology recommendation with rationale before implementation.
- 确定输出类型(静态、动画、交互式、数据驱动)
- 选择渲染技术(Canvas 2D、SVG、WebGL、混合方案)
- 明确数据源和更新频率
- 定义可视化的可访问性要求
- 设定性能预算(帧率、元素数量)
停止 —— 实现前先给出技术选型建议及理由。
Technology Selection Decision Table
技术选型决策表
| Requirement | Canvas 2D | SVG | WebGL |
|---|---|---|---|
| < 1000 elements | Maybe | Yes | No |
| > 10,000 elements | Yes | No | Yes |
| DOM event handling needed | No | Yes | No |
| Pixel manipulation | Yes | No | Yes |
| Text-heavy visualization | No | Yes | No |
| 3D rendering | No | No | Yes |
| Print quality | No | Yes | No |
| Animation-heavy | Yes | Maybe | Yes |
| Accessibility critical | No | Yes | No |
| SEO-relevant content | No | Yes | No |
| 需求 | Canvas 2D | SVG | WebGL |
|---|---|---|---|
| 元素数量<1000 | 可选 | 推荐 | 不推荐 |
| 元素数量>10000 | 推荐 | 不推荐 | 推荐 |
| 需要DOM事件处理 | 不推荐 | 推荐 | 不推荐 |
| 需要像素操作 | 推荐 | 不推荐 | 推荐 |
| 文本密集型可视化 | 不推荐 | 推荐 | 不推荐 |
| 3D渲染 | 不推荐 | 不推荐 | 推荐 |
| 打印质量要求 | 不推荐 | 推荐 | 不推荐 |
| 动画密集型 | 推荐 | 可选 | 推荐 |
| 可访问性为核心要求 | 不推荐 | 推荐 | 不推荐 |
| 内容需要SEO | 不推荐 | 推荐 | 不推荐 |
When to Use Each Technology
各技术适用场景
| Use Case | Recommended | Why |
|---|---|---|
| Dashboard charts (< 500 data points) | SVG + D3.js | DOM events, accessibility, print |
| Real-time data stream (1000+ points) | Canvas 2D | Performance at scale |
| Interactive map with tooltips | SVG | Hover events, accessible |
| Particle system / generative art | Canvas 2D | Pixel-level control, performance |
| 3D data visualization | WebGL (Three.js) | GPU acceleration |
| Infographic for blog post | SVG | Scalable, accessible, printable |
| Game or simulation | Canvas 2D or WebGL | Frame rate, pixel control |
| 使用场景 | 推荐技术 | 原因 |
|---|---|---|
| 仪表板图表(<500个数据点) | SVG + D3.js | 支持DOM事件、可访问性好、可打印 |
| 实时数据流(1000+个数据点) | Canvas 2D | 大规模场景下性能优异 |
| 带提示框的交互式地图 | SVG | 支持悬停事件、可访问性好 |
| 粒子系统/生成艺术 | Canvas 2D | 像素级控制、性能优异 |
| 3D数据可视化 | WebGL (Three.js) | GPU加速支持 |
| 博客信息图 | SVG | 可缩放、可访问性好、可打印 |
| 游戏或模拟程序 | Canvas 2D 或 WebGL | 帧率有保障、支持像素控制 |
Phase 2: Implementation
阶段2:实现
- Set up responsive canvas/SVG container
- Implement rendering pipeline (clear, update, draw)
- Add interaction handlers (hover, click, drag, zoom)
- Optimize render loop (requestAnimationFrame, dirty rectangles)
- Add accessibility layer (ARIA, screen reader descriptions)
STOP — Verify rendering works correctly at target frame rate before adding polish.
- 搭建响应式Canvas/SVG容器
- 实现渲染管线(清空、更新、绘制)
- 添加交互处理逻辑(悬停、点击、拖拽、缩放)
- 优化渲染循环(requestAnimationFrame、脏矩形优化)
- 添加可访问层(ARIA、屏幕阅读器描述)
停止 —— 添加优化内容前先确认渲染在目标帧率下运行正常。
Canvas 2D API Patterns
Canvas 2D API 常用模式
Responsive Canvas Setup
响应式Canvas搭建
javascript
function createResponsiveCanvas(container) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const dpr = window.devicePixelRatio || 1;
function resize() {
const rect = container.getBoundingClientRect();
canvas.width = rect.width * dpr;
canvas.height = rect.height * dpr;
canvas.style.width = `${rect.width}px`;
canvas.style.height = `${rect.height}px`;
ctx.scale(dpr, dpr);
}
const observer = new ResizeObserver(resize);
observer.observe(container);
container.appendChild(canvas);
resize();
return { canvas, ctx, destroy: () => observer.disconnect() };
}javascript
function createResponsiveCanvas(container) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const dpr = window.devicePixelRatio || 1;
function resize() {
const rect = container.getBoundingClientRect();
canvas.width = rect.width * dpr;
canvas.height = rect.height * dpr;
canvas.style.width = `${rect.width}px`;
canvas.style.height = `${rect.height}px`;
ctx.scale(dpr, dpr);
}
const observer = new ResizeObserver(resize);
observer.observe(container);
container.appendChild(canvas);
resize();
return { canvas, ctx, destroy: () => observer.disconnect() };
}Animation Loop Pattern
动画循环模式
javascript
function createAnimationLoop(drawFn) {
let rafId = null;
let lastTime = 0;
function loop(timestamp) {
const deltaTime = timestamp - lastTime;
lastTime = timestamp;
drawFn(deltaTime, timestamp);
rafId = requestAnimationFrame(loop);
}
return {
start: () => { rafId = requestAnimationFrame(loop); },
stop: () => { cancelAnimationFrame(rafId); rafId = null; },
isRunning: () => rafId !== null,
};
}javascript
function createAnimationLoop(drawFn) {
let rafId = null;
let lastTime = 0;
function loop(timestamp) {
const deltaTime = timestamp - lastTime;
lastTime = timestamp;
drawFn(deltaTime, timestamp);
rafId = requestAnimationFrame(loop);
}
return {
start: () => { rafId = requestAnimationFrame(loop); },
stop: () => { cancelAnimationFrame(rafId); rafId = null; },
isRunning: () => rafId !== null,
};
}Dirty Rectangle Optimization
脏矩形优化
javascript
class DirtyRectRenderer {
constructor(ctx, width, height) {
this.ctx = ctx;
this.dirtyRects = [];
this.objects = [];
}
markDirty(x, y, w, h) {
this.dirtyRects.push({ x, y, w, h });
}
render() {
for (const rect of this.dirtyRects) {
this.ctx.clearRect(rect.x, rect.y, rect.w, rect.h);
for (const obj of this.objects) {
if (this.intersects(obj.bounds, rect)) {
obj.draw(this.ctx);
}
}
}
this.dirtyRects = [];
}
}javascript
class DirtyRectRenderer {
constructor(ctx, width, height) {
this.ctx = ctx;
this.dirtyRects = [];
this.objects = [];
}
markDirty(x, y, w, h) {
this.dirtyRects.push({ x, y, w, h });
}
render() {
for (const rect of this.dirtyRects) {
this.ctx.clearRect(rect.x, rect.y, rect.w, rect.h);
for (const obj of this.objects) {
if (this.intersects(obj.bounds, rect)) {
obj.draw(this.ctx);
}
}
}
this.dirtyRects = [];
}
}SVG Patterns
SVG 常用模式
Programmatic SVG Creation
程序化创建SVG
javascript
function createSVG(width, height, viewBox) {
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('width', '100%');
svg.setAttribute('height', '100%');
svg.setAttribute('viewBox', viewBox || `0 0 ${width} ${height}`);
svg.setAttribute('role', 'img');
return svg;
}javascript
function createSVG(width, height, viewBox) {
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('width', '100%');
svg.setAttribute('height', '100%');
svg.setAttribute('viewBox', viewBox || `0 0 ${width} ${height}`);
svg.setAttribute('role', 'img');
return svg;
}SVG Accessibility
SVG可访问性实现
xml
<svg role="img" aria-labelledby="chart-title chart-desc">
<title id="chart-title">Monthly Revenue Chart</title>
<desc id="chart-desc">Bar chart showing revenue from Jan to Dec 2025</desc>
<!-- chart content -->
</svg>xml
<svg role="img" aria-labelledby="chart-title chart-desc">
<title id="chart-title">Monthly Revenue Chart</title>
<desc id="chart-desc">Bar chart showing revenue from Jan to Dec 2025</desc>
<!-- chart content -->
</svg>D3.js Integration Patterns
D3.js 集成模式
Data Join Pattern (D3 v7)
数据关联模式(D3 v7)
javascript
function updateBars(svg, data) {
const bars = svg.selectAll('.bar')
.data(data, d => d.id);
bars.exit()
.transition().duration(300)
.attr('opacity', 0)
.remove();
bars.enter()
.append('rect')
.attr('class', 'bar')
.attr('opacity', 0)
.merge(bars)
.transition().duration(500)
.attr('x', d => xScale(d.label))
.attr('y', d => yScale(d.value))
.attr('width', xScale.bandwidth())
.attr('height', d => height - yScale(d.value))
.attr('opacity', 1);
}javascript
function updateBars(svg, data) {
const bars = svg.selectAll('.bar')
.data(data, d => d.id);
bars.exit()
.transition().duration(300)
.attr('opacity', 0)
.remove();
bars.enter()
.append('rect')
.attr('class', 'bar')
.attr('opacity', 0)
.merge(bars)
.transition().duration(500)
.attr('x', d => xScale(d.label))
.attr('y', d => yScale(d.value))
.attr('width', xScale.bandwidth())
.attr('height', d => height - yScale(d.value))
.attr('opacity', 1);
}Scales and Axes
比例尺和坐标轴
javascript
// Linear scale for continuous data
const yScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)])
.range([height, 0])
.nice();
// Band scale for categorical data
const xScale = d3.scaleBand()
.domain(data.map(d => d.label))
.range([0, width])
.padding(0.2);
// Color scale
const colorScale = d3.scaleOrdinal(d3.schemeTableau10);javascript
// 连续数据的线性比例尺
const yScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)])
.range([height, 0])
.nice();
// 分类数据的带状比例尺
const xScale = d3.scaleBand()
.domain(data.map(d => d.label))
.range([0, width])
.padding(0.2);
// 颜色比例尺
const colorScale = d3.scaleOrdinal(d3.schemeTableau10);Responsive D3 Chart
响应式D3图表
javascript
function responsiveChart(container, renderFn) {
const observer = new ResizeObserver(entries => {
const { width, height } = entries[0].contentRect;
const margin = { top: 20, right: 20, bottom: 40, left: 50 };
renderFn(container, {
width: width - margin.left - margin.right,
height: height - margin.top - margin.bottom,
margin,
});
});
observer.observe(container);
return () => observer.disconnect();
}javascript
function responsiveChart(container, renderFn) {
const observer = new ResizeObserver(entries => {
const { width, height } = entries[0].contentRect;
const margin = { top: 20, right: 20, bottom: 40, left: 50 };
renderFn(container, {
width: width - margin.left - margin.right,
height: height - margin.top - margin.bottom,
margin,
});
});
observer.observe(container);
return () => observer.disconnect();
}Phase 3: Polish
阶段3:优化打磨
- Add transitions and easing
- Implement responsive resizing
- Test across devices and browsers
- Add fallback content for unsupported environments
STOP — Verify accessibility and performance targets are met.
- 添加过渡动画和缓动效果
- 实现响应式缩放
- 跨设备和浏览器测试
- 为不支持的环境添加降级内容
停止 —— 确认可访问性和性能指标均达标。
Generative Art Techniques
生成艺术技术
| Technique | Category | Use Case |
|---|---|---|
| Perlin/Simplex noise | Noise-based | Organic shapes, terrain |
| Fractal Brownian Motion | Noise-based | Terrain-like textures |
| Domain warping | Noise-based | Fluid-like effects |
| Particle systems | Simulation | Fire, water, swarms |
| L-systems | Algorithmic | Plant-like structures |
| Voronoi diagrams | Algorithmic | Cell-like partitions |
| Delaunay triangulation | Algorithmic | Mesh generation |
| Flow fields | Algorithmic | Directional patterns |
| 技术 | 类别 | 使用场景 |
|---|---|---|
| Perlin/Simplex 噪声 | 噪声类 | 有机形状、地形 |
| 分形布朗运动 | 噪声类 | 类地形纹理 |
| 域扭曲 | 噪声类 | 流体类效果 |
| 粒子系统 | 模拟类 | 火焰、水流、群集 |
| L-系统 | 算法类 | 类植物结构 |
| Voronoi 图 | 算法类 | 类细胞分区 |
| Delaunay 三角剖分 | 算法类 | 网格生成 |
| 流场 | 算法类 | 方向性图案 |
Accessibility for Visualizations
可视化可访问性要求
| Requirement | Implementation |
|---|---|
| Text alternatives | |
| Data table fallback | Hidden table with same data for screen readers |
| Keyboard interaction | Tab to elements, arrow keys for navigation |
| Color-blind safe | Use shape/pattern in addition to color |
| High contrast mode | Detect and adjust colors |
| Reduced motion | Simplify or disable animations with |
| 要求 | 实现方式 |
|---|---|
| 文本替代内容 | SVG中添加 |
| 数据表格降级 | 为屏幕阅读器隐藏存储相同数据的表格 |
| 键盘交互 | 支持Tab选中元素、方向键导航 |
| 色弱友好 | 除颜色外额外使用形状/图案区分内容 |
| 高对比度模式支持 | 检测模式并调整颜色 |
| 减少动画 | 通过 |
Performance Guidelines
性能指南
| Technology | Optimization |
|---|---|
| Canvas | Batch draw calls, minimize state changes (fill/stroke/font) |
| SVG | Limit to < 1000 DOM nodes, use |
| Worker | OffscreenCanvas for web worker rendering |
| Events | Throttle mousemove handlers to 16ms (60fps) |
| CSS | Use |
| Cleanup | Dispose of observers, animation frames on unmount |
| 技术 | 优化方案 |
|---|---|
| Canvas | 批量绘制调用,减少状态变更(填充/描边/字体) |
| SVG | 限制DOM节点数量<1000,重复元素使用 |
| Worker | 使用OffscreenCanvas在Web Worker中渲染 |
| 事件 | 将mousemove处理程序节流到16ms(60fps) |
| CSS | CSS动画的SVG元素添加 |
| 清理 | 卸载时销毁观察者、取消动画帧 |
Anti-Patterns / Common Mistakes
反模式/常见错误
| Anti-Pattern | Why It Is Wrong | What to Do Instead |
|---|---|---|
| Canvas for text-heavy content | Poor text rendering, no accessibility | Use SVG for text-heavy visualizations |
| Full canvas redraw every frame | Wastes CPU when nothing changed | Use dirty rectangle optimization |
| Creating new SVG elements instead of updating | Memory leaks, poor performance | Use D3 data join pattern |
| Forgetting devicePixelRatio | Blurry on Retina/HiDPI displays | Scale canvas by |
| No fallback content for Canvas | Screen readers get nothing | Add |
| Inline event handlers on 1000+ SVG elements | Memory and performance overhead | Use event delegation on parent |
| Not cleaning up animation frames | Memory leaks on unmount | |
| Using D3 for simple static charts | Over-engineering | Use SVG directly or Chart.js |
| 反模式 | 问题 | 替代方案 |
|---|---|---|
| 文本密集内容使用Canvas | 文本渲染效果差、无访问性 | 文本密集可视化使用SVG |
| 每帧全量重绘Canvas | 无内容变更时浪费CPU | 使用脏矩形优化 |
| 频繁新建SVG元素而非更新 | 内存泄漏、性能差 | 使用D3数据关联模式 |
| 忽略devicePixelRatio | Retina/高DPI屏幕下显示模糊 | 按 |
| Canvas无降级内容 | 屏幕阅读器无法识别 | 添加 |
| 1000+个SVG元素绑定内联事件 | 内存和性能开销大 | 父元素使用事件委托 |
| 未清理动画帧 | 卸载时内存泄漏 | 清理逻辑中调用 |
| 简单静态图表使用D3 | 过度设计 | 直接使用SVG或Chart.js |
Integration Points
集成点
| Skill | Integration |
|---|---|
| Chart type selection and color palettes |
| Design tokens for chart theming |
| Self-contained visualization artifacts |
| Component integration in React/Vue/Svelte |
| Frame rate and rendering optimization |
| Touch interaction for mobile charts |
| 技能 | 集成场景 |
|---|---|
| 图表类型选择和调色板 |
| 图表主题的设计令牌 |
| 独立可视化产物 |
| React/Vue/Svelte中的组件集成 |
| 帧率和渲染优化 |
| 移动端图表的触摸交互 |
Skill Type
技能类型
FLEXIBLE — Choose the rendering technology and library that best fits the use case. SVG for accessibility-critical and interactive charts, Canvas for performance-critical and animation-heavy visuals, WebGL for 3D and massive datasets.
灵活适配 —— 选择最适合场景的渲染技术和库。可访问性要求高、交互式的图表使用SVG,性能要求高、动画密集的视觉内容使用Canvas,3D和超大规模数据集使用WebGL。