canvas-design について
HTML Canvas 2D、SVG、WebGLやD3.jsなどの可視化ライブラリで、グラフィックやデータ可視化を作成するためのデザインスキルです。開発者が描画方式を選び、実装時の操作性、アクセシビリティ、レスポンシブ対応、性能を検討できるよう支援します。
主な機能
- 01要件に応じたCanvas、SVG、WebGLの選択
- 02D3.jsのパターンによるチャート作成
- 03操作性とレスポンシブなサイズ調整
- 04アクセシビリティと代替コンテンツの設計
- 05描画とアニメーション性能の最適化
活用シーン
- 01可視化に適した描画方式の選定
- 02操作可能なチャートやマップの作成
- 03ジェネレーティブアートやアニメーションの制作
- 04アクセシビリティと描画性能の改善
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ pixel-process-ug/ superkit-agents --skill canvas-designインストール後、Claude Code で/canvas-designを入力して読み込まれたか確認します。
詳細
Canvas デザイン
概要
HTML Canvas 2D、SVG、データ可視化ライブラリを使用して、高性能でアクセシブル、かつ視覚的に魅力的なグラフィックを作成します。このスキルでは、低レベルのピクセル操作から、D3.js を使った高度なチャート構成までを網羅し、ジェネレーティブアート、インタラクティブなグラフィック、アニメーションも扱います。
フェーズ 1:要件分析
- 出力の種類(静的、アニメーション、インタラクティブ、データ駆動型)を決定します
- レンダリング技術(Canvas 2D、SVG、WebGL、ハイブリッド)を選択します
- データソースと更新頻度を特定します
- 可視化に必要なアクセシビリティ要件を定義します
- パフォーマンス予算(フレームレート、要素数)を設定します
STOP — 実装の前に、根拠を添えて技術の推奨を提示してください。
技術選択の判断表
| 要件 | Canvas 2D | SVG | WebGL |
|---|---|---|---|
| < 1000 要素 | 場合による | はい | いいえ |
| > 10,000 要素 | はい | いいえ | はい |
| DOM イベント処理が必要 | いいえ | はい | いいえ |
| ピクセル操作 | はい | いいえ | はい |
| テキスト中心の可視化 | いいえ | はい | いいえ |
| 3D レンダリング | いいえ | いいえ | はい |
| 印刷品質 | いいえ | はい | いいえ |
| アニメーションが多い | はい | 場合による | はい |
| アクセシビリティが重要 | いいえ | はい | いいえ |
| SEO に関連するコンテンツ | いいえ | はい | いいえ |
各技術の使いどころ
| 使用例 | 推奨 | 理由 |
|---|---|---|
| ダッシュボードのチャート(データポイント < 500) | SVG + D3.js | DOM イベント、アクセシビリティ、印刷 |
| リアルタイムのデータストリーム(データポイント 1000 以上) | Canvas 2D | 大規模でも高いパフォーマンス |
| ツールチップ付きのインタラクティブな地図 | SVG | ホバーイベント、アクセシビリティ |
| パーティクルシステム/ジェネレーティブアート | Canvas 2D | ピクセルレベルの制御、パフォーマンス |
| 3D データ可視化 | WebGL (Three.js) | GPU アクセラレーション |
| ブログ記事用のインフォグラフィック | SVG | 拡大縮小可能、アクセシブル、印刷可能 |
| ゲームまたはシミュレーション | Canvas 2D または WebGL | フレームレート、ピクセル制御 |
フェーズ 2:実装
- レスポンシブな Canvas/SVG コンテナーを設定します
- レンダリングパイプライン(クリア、更新、描画)を実装します
- インタラクションハンドラー(ホバー、クリック、ドラッグ、ズーム)を追加します
- レンダリングループ(requestAnimationFrame、ダーティレクタングル)を最適化します
- アクセシビリティレイヤー(ARIA、スクリーンリーダー向けの説明)を追加します
STOP — 装飾を加える前に、目標フレームレートで正しくレンダリングされることを確認してください。
Canvas 2D API パターン
レスポンシブな Canvas のセットアップ
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() };
}
アニメーションループのパターン
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,
};
}
ダーティレクタングルによる最適化
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 パターン
プログラムによる SVG の作成
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 のアクセシビリティ
<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>
</svg>
D3.js 統合パターン
データ結合パターン(D3 v7)
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);
}
スケールと軸
// 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);
レスポンシブな D3 チャート
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();
}
フェーズ 3:仕上げ
- トランジションとイージングを追加します
- レスポンシブなサイズ変更を実装します
- デバイスやブラウザーをまたいでテストします
- 非対応の環境向けに代替コンテンツを追加します
STOP — アクセシビリティとパフォーマンスの目標を満たしていることを確認してください。
ジェネレーティブアートの手法
| 手法 | カテゴリ | 使用例 |
|---|---|---|
| Perlin/Simplex ノイズ | ノイズベース | 有機的な形状、地形 |
| フラクタルブラウン運動 | ノイズベース | 地形のようなテクスチャ |
| ドメインワーピング | ノイズベース | 流体のような効果 |
| パーティクルシステム | シミュレーション | 火、水、群れ |
| L システム | アルゴリズム | 植物のような構造 |
| ボロノイ図 | アルゴリズム | 細胞のような分割 |
| ドロネー三角形分割 | アルゴリズム | メッシュ生成 |
| フローフィールド | アルゴリズム | 方向性のあるパターン |
可視化のアクセシビリティ
| 要件 | 実装 |
|---|---|
| テキストによる代替 | SVG の <title> と <desc>、Canvas の ARIA ラベル |
| データテーブルによる代替 | スクリーンリーダー用に同じデータを含む非表示テーブル |
| キーボード操作 | 要素への Tab 移動、ナビゲーション用の矢印キー |
| 色覚多様性への配慮 | 色に加えて形状/パターンを使用 |
| ハイコントラストモード | 検出して色を調整 |
| 動きの低減 | prefers-reduced-motion を使ってアニメーションを簡略化または無効化 |
パフォーマンスのガイドライン
| 技術 | 最適化 |
|---|---|
| Canvas | 描画呼び出しをまとめ、状態の変更(塗りつぶし/線/フォント)を最小限に抑えます |
| SVG | DOM ノードを 1000 未満に抑え、繰り返し要素には <use> を使用します |
| Worker | Web ワーカーでの描画には OffscreenCanvas を使用します |
| イベント | mousemove ハンドラーを 16ms(60fps)間隔でスロットリングします |
| CSS | CSS でアニメーションする SVG 要素には will-change: transform を使用します |
| クリーンアップ | アンマウント時にオブザーバーとアニメーションフレームを破棄します |
アンチパターン/よくある間違い
| アンチパターン | 誤りである理由 | 代わりにすること |
|---|---|---|
| テキスト中心のコンテンツに Canvas を使う | テキストの描画品質が低く、アクセシビリティにも対応できません | テキスト中心の可視化には SVG を使用します |
| 毎フレーム、Canvas 全体を再描画する | 何も変わっていないときにも CPU を浪費します | ダーティー矩形による最適化を使用します |
| 更新する代わりに、新しい SVG 要素を作成する | メモリーリークやパフォーマンス低下の原因になります | D3 のデータ結合パターンを使用します |
| devicePixelRatio を考慮しない | Retina/HiDPI ディスプレイでぼやけます | window.devicePixelRatio に合わせて Canvas を拡大縮小します |
| Canvas にフォールバックコンテンツがない | スクリーンリーダーに何も伝わりません | aria-label と非表示のデータテーブルを追加します |
| 1000 個以上の SVG 要素にインラインイベントハンドラーを設定する | メモリーとパフォーマンスに負荷がかかります | 親要素でイベント委譲を使用します |
| アニメーションフレームをクリーンアップしない | アンマウント時にメモリーリークが発生します | クリーンアップで cancelAnimationFrame を呼び出します |
| シンプルな静的グラフに D3 を使う | 過剰な設計になります | SVG を直接使うか、Chart.js を使用します |
統合ポイント
| スキル | 統合 |
|---|---|
ui-ux-pro-max | グラフの種類の選定とカラーパレット |
ui-design-system | グラフのテーマ設定に使うデザイントークン |
artifacts-builder | 自己完結型の可視化アーティファクト |
senior-frontend | React/Vue/Svelte でのコンポーネント統合 |
performance-optimization | フレームレートと描画の最適化 |
mobile-design | モバイルグラフのタッチ操作 |
スキルの種類
FLEXIBLE — 用途に最適な描画技術とライブラリを選択します。アクセシビリティが重要なグラフやインタラクティブなグラフには SVG、パフォーマンスが重要なビジュアルやアニメーションを多用するビジュアルには Canvas、3D や大規模なデータセットには WebGL を使用します。
コメント
Markdown 対応