HTMLで図表を作る方法

HTMLで図やグラフを作る方法は主に4つあります。それぞれの特徴と実装例を見ていきましょう。

4つの手法

  • 📊 CSS: シンプルな棒グラフ、円グラフ
  • 🎨 SVG: ベクター図形、フローチャート
  • 🖼️ Canvas: 複雑なグラフ、アニメーション
  • 📐 ライブラリ: Chart.js、Mermaid、D3.js

1. CSSで作る図表

最もシンプルで軽量な方法です。

棒グラフの実装

<!-- HTML -->
<div class="chart-container">
  <div class="bar-chart">
    <div class="bar" style="--value: 60%;">
      <span class="label">2023</span>
      <span class="value">60%</span>
    </div>
    <div class="bar" style="--value: 75%;">
      <span class="label">2024</span>
      <span class="value">75%</span>
    </div>
    <div class="bar" style="--value: 90%;">
      <span class="label">2025</span>
      <span class="value">90%</span>
    </div>
  </div>
</div>

<!-- CSS -->
<style>
.chart-container {
  padding: 2rem;
  background: white;
  border: 3px solid #2a2a2a;
  box-shadow: 5px 5px 0px rgba(0,0,0,0.1);
}

.bar-chart {
  display: flex;
  gap: 1.5rem;
  height: 250px;
  align-items: flex-end;
  padding: 1rem;
}

.bar {
  flex: 1;
  height: var(--value);
  background: linear-gradient(to top, #ffeb3b, #fdd835);
  border: 3px solid #2a2a2a;
  box-shadow: 3px 3px 0px rgba(0,0,0,0.2);
  position: relative;
  transition: all 0.3s;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: center;
  padding: 0.5rem;
}

.bar:hover {
  transform: translateY(-5px);
  box-shadow: 5px 5px 0px rgba(0,0,0,0.3);
}

.bar .value {
  font-weight: bold;
  color: #2a2a2a;
  font-size: 1.1rem;
}

.bar .label {
  position: absolute;
  bottom: -2rem;
  font-weight: bold;
  color: #666;
}
</style>

実装結果

60%2023
75%2024
90%2025

円グラフの実装

<!-- HTML -->
<div class="pie-chart-container">
  <div class="pie-chart">
    <div class="slice slice-1"></div>
    <div class="slice slice-2"></div>
    <div class="slice slice-3"></div>
  </div>
  <div class="legend">
    <div class="legend-item">
      <span class="color" style="background: #ffeb3b;"></span>
      <span>HTML 40%</span>
    </div>
    <div class="legend-item">
      <span class="color" style="background: #4caf50;"></span>
      <span>CSS 35%</span>
    </div>
    <div class="legend-item">
      <span class="color" style="background: #2196f3;"></span>
      <span>JavaScript 25%</span>
    </div>
  </div>
</div>

<!-- CSS -->
<style>
.pie-chart-container {
  display: flex;
  gap: 2rem;
  align-items: center;
  padding: 2rem;
  background: white;
  border: 3px solid #2a2a2a;
}

.pie-chart {
  width: 200px;
  height: 200px;
  border-radius: 50%;
  background: conic-gradient(
    #ffeb3b 0% 40%,
    #4caf50 40% 75%,
    #2196f3 75% 100%
  );
  border: 3px solid #2a2a2a;
  box-shadow: 4px 4px 0px rgba(0,0,0,0.2);
}

.legend {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.legend-item {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  font-weight: bold;
}

.legend-item .color {
  width: 30px;
  height: 30px;
  border: 2px solid #2a2a2a;
  box-shadow: 2px 2px 0px rgba(0,0,0,0.2);
}
</style>

実装結果

HTML 40%
CSS 35%
JavaScript 25%

プログレスバー

<div class="progress-bar">
  <div class="progress-fill" style="width: 75%;">75%</div>
</div>

<style>
.progress-bar {
  width: 100%;
  height: 40px;
  background: #f5f5f5;
  border: 3px solid #2a2a2a;
  position: relative;
  overflow: hidden;
}

.progress-fill {
  height: 100%;
  background: linear-gradient(90deg, #ffeb3b, #fdd835);
  border-right: 3px solid #2a2a2a;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: bold;
  color: #2a2a2a;
  transition: width 1s ease;
}
</style>

実装結果

75%

CSSのメリット・デメリット

  • ✅ 軽量(追加ライブラリ不要)
  • ✅ レスポンシブ対応が簡単
  • ✅ CSSアニメーション可能
  • ❌ 複雑な図形は難しい
  • ❌ 動的データの更新が面倒

2. SVGで作る図表

ベクター形式なので拡大縮小しても綺麗です。

フローチャートの実装

<svg width="400" height="300" style="border: 3px solid #2a2a2a; background: white;">
  <!-- 矢印のマーカー定義 -->
  <defs>
    <marker id="arrow" markerWidth="10" markerHeight="10" 
            refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
      <path d="M0,0 L0,6 L9,3 z" fill="#2a2a2a" />
    </marker>
  </defs>
  
  <!-- 開始ノード -->
  <rect x="130" y="20" width="140" height="50" 
        fill="#ffeb3b" stroke="#2a2a2a" stroke-width="3" />
  <text x="200" y="50" text-anchor="middle" 
        font-size="16" font-weight="bold">開始</text>
  
  <!-- 矢印1 -->
  <line x1="200" y1="70" x2="200" y2="100" 
        stroke="#2a2a2a" stroke-width="3" marker-end="url(#arrow)" />
  
  <!-- 処理ノード -->
  <rect x="130" y="100" width="140" height="50" 
        fill="#4caf50" stroke="#2a2a2a" stroke-width="3" />
  <text x="200" y="130" text-anchor="middle" 
        font-size="16" font-weight="bold" fill="white">処理</text>
  
  <!-- 矢印2 -->
  <line x1="200" y1="150" x2="200" y2="180" 
        stroke="#2a2a2a" stroke-width="3" marker-end="url(#arrow)" />
  
  <!-- 判断ノード(ひし形) -->
  <path d="M 200,180 L 270,210 L 200,240 L 130,210 Z" 
        fill="#2196f3" stroke="#2a2a2a" stroke-width="3" />
  <text x="200" y="215" text-anchor="middle" 
        font-size="16" font-weight="bold" fill="white">判断</text>
  
  <!-- Yes矢印 -->
  <line x1="270" y1="210" x2="320" y2="210" 
        stroke="#2a2a2a" stroke-width="3" marker-end="url(#arrow)" />
  <text x="295" y="205" font-size="14" font-weight="bold">Yes</text>
  
  <!-- No矢印 -->
  <line x1="130" y1="210" x2="80" y2="210" 
        stroke="#2a2a2a" stroke-width="3" marker-end="url(#arrow)" />
  <text x="105" y="205" font-size="14" font-weight="bold">No</text>
</svg>

実装結果

開始処理判断YesNo

アイコンとグラフィック

<svg width="300" height="100" style="border: 3px solid #2a2a2a; background: white;">
  <!-- チェックマーク -->
  <circle cx="50" cy="50" r="30" fill="#4caf50" stroke="#2a2a2a" stroke-width="3" />
  <path d="M 35,50 L 45,60 L 65,35" stroke="white" stroke-width="5" 
        fill="none" stroke-linecap="round" stroke-linejoin="round" />
  
  <!-- エラーマーク -->
  <circle cx="150" cy="50" r="30" fill="#f44336" stroke="#2a2a2a" stroke-width="3" />
  <line x1="135" y1="35" x2="165" y2="65" stroke="white" stroke-width="5" stroke-linecap="round" />
  <line x1="165" y1="35" x2="135" y2="65" stroke="white" stroke-width="5" stroke-linecap="round" />
  
  <!-- 警告マーク -->
  <path d="M 250,25 L 280,75 L 220,75 Z" fill="#ffeb3b" stroke="#2a2a2a" stroke-width="3" />
  <text x="250" y="60" text-anchor="middle" font-size="30" font-weight="bold">!</text>
</svg>

実装結果

!

折れ線グラフ

<svg width="400" height="200" style="border: 3px solid #2a2a2a; background: white;">
  <!-- グリッド線 -->
  <line x1="50" y1="20" x2="50" y2="160" stroke="#ccc" stroke-width="2" />
  <line x1="50" y1="160" x2="380" y2="160" stroke="#ccc" stroke-width="2" />
  
  <!-- 折れ線 -->
  <polyline points="50,140 120,100 190,80 260,110 330,50" 
            fill="none" stroke="#2196f3" stroke-width="4" />
  
  <!-- データポイント -->
  <circle cx="50" cy="140" r="6" fill="#2196f3" stroke="#2a2a2a" stroke-width="2" />
  <circle cx="120" cy="100" r="6" fill="#2196f3" stroke="#2a2a2a" stroke-width="2" />
  <circle cx="190" cy="80" r="6" fill="#2196f3" stroke="#2a2a2a" stroke-width="2" />
  <circle cx="260" cy="110" r="6" fill="#2196f3" stroke="#2a2a2a" stroke-width="2" />
  <circle cx="330" cy="50" r="6" fill="#2196f3" stroke="#2a2a2a" stroke-width="2" />
  
  <!-- ラベル -->
  <text x="50" y="180" text-anchor="middle" font-size="12">1月</text>
  <text x="120" y="180" text-anchor="middle" font-size="12">2月</text>
  <text x="190" y="180" text-anchor="middle" font-size="12">3月</text>
  <text x="260" y="180" text-anchor="middle" font-size="12">4月</text>
  <text x="330" y="180" text-anchor="middle" font-size="12">5月</text>
</svg>

実装結果

1月2月3月4月5月

SVGのメリット・デメリット

  • ✅ ベクター形式(拡大縮小しても綺麗)
  • ✅ CSS/JavaScriptで操作可能
  • ✅ アニメーション対応
  • ✅ テキスト選択可能
  • ❌ 複雑な図形はコードが長くなる
  • ❌ 大量の要素はパフォーマンス低下

3. Canvasで作る図表

JavaScriptで動的に描画します。複雑なグラフやアニメーションに最適。

基本的な図形

<canvas id="basicCanvas" width="400" height="200" 
        style="border: 3px solid #2a2a2a; background: white;"></canvas>

<script>
const canvas = document.getElementById('basicCanvas');
const ctx = canvas.getContext('2d');

// 四角形
ctx.fillStyle = '#ffeb3b';
ctx.strokeStyle = '#2a2a2a';
ctx.lineWidth = 3;
ctx.fillRect(30, 30, 100, 80);
ctx.strokeRect(30, 30, 100, 80);

// 円
ctx.fillStyle = '#4caf50';
ctx.beginPath();
ctx.arc(220, 70, 40, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();

// 線
ctx.strokeStyle = '#2196f3';
ctx.lineWidth = 5;
ctx.beginPath();
ctx.moveTo(300, 40);
ctx.lineTo(370, 100);
ctx.stroke();

// テキスト
ctx.fillStyle = '#2a2a2a';
ctx.font = 'bold 16px sans-serif';
ctx.fillText('Canvas', 30, 160);
</script>

実装結果

棒グラフの実装

<canvas id="barCanvas" width="400" height="250" 
        style="border: 3px solid #2a2a2a; background: white;"></canvas>

<script>
const barCanvas = document.getElementById('barCanvas');
const barCtx = barCanvas.getContext('2d');

const data = [
  { label: 'HTML', value: 85, color: '#ffeb3b' },
  { label: 'CSS', value: 70, color: '#4caf50' },
  { label: 'JavaScript', value: 90, color: '#2196f3' },
  { label: 'React', value: 65, color: '#9c27b0' }
];

const barWidth = 60;
const spacing = 30;
const maxHeight = 150;
const startX = 40;
const startY = 200;

data.forEach((item, index) => {
  const x = startX + index * (barWidth + spacing);
  const height = (item.value / 100) * maxHeight;
  const y = startY - height;
  
  // 棒
  barCtx.fillStyle = item.color;
  barCtx.fillRect(x, y, barWidth, height);
  barCtx.strokeStyle = '#2a2a2a';
  barCtx.lineWidth = 3;
  barCtx.strokeRect(x, y, barWidth, height);
  
  // ラベル
  barCtx.fillStyle = '#2a2a2a';
  barCtx.font = 'bold 12px sans-serif';
  barCtx.textAlign = 'center';
  barCtx.fillText(item.label, x + barWidth / 2, startY + 20);
  
  // 値
  barCtx.fillStyle = '#2a2a2a';
  barCtx.font = 'bold 14px sans-serif';
  barCtx.fillText(item.value, x + barWidth / 2, y - 10);
});
</script>

実装結果

円グラフの実装

<canvas id="pieCanvas" width="400" height="300" 
        style="border: 3px solid #2a2a2a; background: white;"></canvas>

<script>
const pieCanvas = document.getElementById('pieCanvas');
const pieCtx = pieCanvas.getContext('2d');

const pieData = [
  { label: 'React', value: 35, color: '#2196f3' },
  { label: 'Vue', value: 25, color: '#4caf50' },
  { label: 'Angular', value: 20, color: '#f44336' },
  { label: 'その他', value: 20, color: '#ffeb3b' }
];

const centerX = 150;
const centerY = 150;
const radius = 100;
let currentAngle = -Math.PI / 2;

pieData.forEach((item, index) => {
  const sliceAngle = (item.value / 100) * Math.PI * 2;
  
  // スライス描画
  pieCtx.fillStyle = item.color;
  pieCtx.beginPath();
  pieCtx.moveTo(centerX, centerY);
  pieCtx.arc(centerX, centerY, radius, currentAngle, currentAngle + sliceAngle);
  pieCtx.closePath();
  pieCtx.fill();
  
  // 枠線
  pieCtx.strokeStyle = '#2a2a2a';
  pieCtx.lineWidth = 3;
  pieCtx.stroke();
  
  currentAngle += sliceAngle;
});

// 凡例
let legendY = 50;
pieData.forEach((item) => {
  pieCtx.fillStyle = item.color;
  pieCtx.fillRect(280, legendY, 25, 25);
  pieCtx.strokeStyle = '#2a2a2a';
  pieCtx.lineWidth = 2;
  pieCtx.strokeRect(280, legendY, 25, 25);
  
  pieCtx.fillStyle = '#2a2a2a';
  pieCtx.font = 'bold 14px sans-serif';
  pieCtx.textAlign = 'left';
  pieCtx.fillText(item.label + ' ' + item.value + '%', 315, legendY + 18);
  
  legendY += 40;
});
</script>

実装結果

Canvasのメリット・デメリット

  • ✅ 高度なグラフィックス
  • ✅ アニメーション・ゲームに最適
  • ✅ 大量のデータも高速描画
  • ✅ ピクセル単位の操作可能
  • ❌ SEOに不利(テキストがない)
  • ❌ アクセシビリティが低い
  • ❌ レスポンシブ対応が面倒

4. ライブラリを使う方法

Chart.js(最も人気)

<!-- CDN読み込み -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

<canvas id="chartjs" width="400" height="200"></canvas>

<script>
const ctx = document.getElementById('chartjs').getContext('2d');
const myChart = new Chart(ctx, {
  type: 'bar',
  data: {
    labels: ['1月', '2月', '3月', '4月', '5月'],
    datasets: [{
      label: '売上(万円)',
      data: [12, 19, 8, 15, 22],
      backgroundColor: [
        '#ffeb3b', '#4caf50', '#2196f3', '#9c27b0', '#f44336'
      ],
      borderColor: '#2a2a2a',
      borderWidth: 3
    }]
  },
  options: {
    responsive: true,
    plugins: {
      legend: {
        display: true,
        position: 'top'
      }
    },
    scales: {
      y: {
        beginAtZero: true
      }
    }
  }
});
</script>

Chart.jsの特徴

  • ✅ 簡単に美しいグラフ作成
  • ✅ レスポンシブ対応
  • ✅ アニメーション標準装備
  • ✅ 多様なグラフタイプ
  • ❌ ライブラリのサイズが大きい(約200KB)

Mermaid(ダイアグラム専用)

<!-- CDN読み込み -->
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
<script>mermaid.initialize({startOnLoad:true});</script>

<!-- フローチャート -->
<div class="mermaid">
flowchart TD
    A[開始] --> B{条件判定}
    B -->|Yes| C[処理A]
    B -->|No| D[処理B]
    C --> E[終了]
    D --> E
</div>

<!-- シーケンス図 -->
<div class="mermaid">
sequenceDiagram
    participant U as ユーザー
    participant S as サーバー
    participant DB as データベース
    
    U->>S: リクエスト
    S->>DB: クエリ
    DB-->>S: データ
    S-->>U: レスポンス
</div>

<!-- ガントチャート -->
<div class="mermaid">
gantt
    title プロジェクト計画
    dateFormat YYYY-MM-DD
    section 設計
    要件定義 :a1, 2025-01-01, 7d
    基本設計 :a2, after a1, 10d
    section 開発
    実装     :b1, after a2, 20d
    テスト   :b2, after b1, 10d
</div>

Mermaidの特徴

  • ✅ テキストで簡単に図を作成
  • ✅ フローチャート、シーケンス図、ガントチャートなど
  • ✅ マークダウンと相性が良い
  • ✅ GitHubでも使える
  • ❌ カスタマイズの自由度は低い

比較表

手法難易度柔軟性パフォーマンス適した用途
CSS⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️シンプルな棒グラフ、プログレスバー
SVG⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️フローチャート、アイコン、ロゴ
Canvas⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️複雑なグラフ、ゲーム、アニメーション
Chart.js⭐️⭐️⭐️⭐️⭐️⭐️⭐️データ可視化、ダッシュボード
Mermaid⭐️⭐️⭐️⭐️⭐️⭐️フローチャート、シーケンス図、ドキュメント

使い分けのポイント

CSSを選ぶべき場合

  • シンプルな棒グラフやプログレスバー
  • ライブラリを使いたくない
  • 軽量さを重視
  • 静的なデータ

SVGを選ぶべき場合

  • フローチャート、ダイアグラム
  • アイコン、ロゴ
  • 拡大縮小が必要
  • CSSアニメーション
  • アクセシビリティ重視

Canvasを選ぶべき場合

  • 複雑なグラフ
  • ゲーム、アニメーション
  • 大量のデータ
  • リアルタイム更新
  • 画像エフェクト

ライブラリを選ぶべき場合

  • 開発時間を短縮したい
  • プロフェッショナルな見た目
  • 複雑なインタラクション
  • レスポンシブ対応

実践例:ダッシュボード

複数の手法を組み合わせた実例です。

<div class="dashboard">
  <!-- CSSプログレスバー -->
  <div class="widget">
    <h3>タスク進捗</h3>
    <div class="progress-bar">
      <div class="progress-fill" style="width: 68%;">68%</div>
    </div>
  </div>
  
  <!-- SVGアイコン -->
  <div class="widget">
    <h3>ステータス</h3>
    <svg width="100" height="100">
      <circle cx="50" cy="50" r="40" fill="#4caf50" />
      <path d="M 30,50 L 45,65 L 70,35" stroke="white" stroke-width="5" fill="none" />
    </svg>
  </div>
  
  <!-- Canvas グラフ -->
  <div class="widget">
    <h3>売上推移</h3>
    <canvas id="salesChart"></canvas>
  </div>
</div>

<style>
.dashboard {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 1.5rem;
}

.widget {
  background: white;
  border: 3px solid #2a2a2a;
  padding: 1.5rem;
  box-shadow: 4px 4px 0px rgba(0,0,0,0.1);
}

.widget h3 {
  margin-bottom: 1rem;
  color: #2a2a2a;
  border-bottom: 2px dashed #ccc;
  padding-bottom: 0.5rem;
}
</style>

パフォーマンスの最適化

CSS

/* GPUアクセラレーション */
.animated-element {
  transform: translateZ(0);
  will-change: transform;
}

/* 不要な再描画を避ける */
.static-element {
  contain: layout style paint;
}

SVG

<!-- 複雑なパスを簡略化 -->
<!-- 使わない属性は削除 -->
<!-- viewBoxでサイズ指定 -->
<svg viewBox="0 0 100 100" width="200" height="200">
  <!-- ... -->
</svg>

Canvas

// リサイズ時のパフォーマンス
function resizeCanvas() {
  const dpr = window.devicePixelRatio || 1;
  canvas.width = canvas.offsetWidth * dpr;
  canvas.height = canvas.offsetHeight * dpr;
  ctx.scale(dpr, dpr);
}

// 部分的な再描画
function clearRect(x, y, w, h) {
  ctx.clearRect(x, y, w, h);
  // 必要な部分だけ再描画
}

アクセシビリティ

SVG

<svg role="img" aria-labelledby="chartTitle">
  <title id="chartTitle">2025年の売上推移</title>
  <desc>1月から5月までの月次売上を示す棒グラフ</desc>
  <!-- グラフの内容 -->
</svg>

Canvas

<canvas id="chart" role="img" aria-label="売上グラフ">
  <!-- フォールバックコンテンツ -->
  <p>売上データ: 1月 12万円, 2月 19万円, 3月 8万円...</p>
</canvas>

まとめ

目的に応じて最適な手法を選択しましょう。

クイックリファレンス

  • 📊 シンプルな棒グラフ → CSS
  • 🎨 フローチャート → SVG または Mermaid
  • 📈 データ可視化 → Chart.js
  • 🎮 ゲーム・複雑なアニメーション → Canvas
  • 📐 ドキュメント内の図 → Mermaid
  • 🎯 アイコン・ロゴ → SVG

学習のステップ

  1. CSSで基礎を学ぶ(box-model、positioning)
  2. SVGで図形を理解する(path、circle、rect)
  3. Canvasで描画APIを習得(fillRect、arc、stroke)
  4. ライブラリで実践(Chart.js、Mermaid)

実際のプロジェクトでは複数の手法を組み合わせて使うことが多いです。