Chart.js を使った横棒グラフのカスタマイズ メモ

# Chart.jsを使って横棒グラフを作成しよう！ 
この記事ではChart.jsを利用した横棒グラフの作成とカスタマイズをする方法を記載しています。

## 完成イメージ
以下のような横棒グラフを作成します。

![スクリーンショット 2024-12-28 0.24.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2958368/91474016-78a9-733a-78c2-d26149bd5d95.png)

## グラフの特徴
今回は以下のような特徴のグラフを作成しています。

1.横棒グラフの作成と背景色の設定
- 横方向の棒グラフを描画し、背景色を設定。

2.バーの横に点線を表示
- 各バーの右端からラベルまで点線を描画。

3.数値ラベルを右側に表示
- 点線の右端に数値を描画。

4.入力データの反映
- ユーザーがフォームに入力したデータをグラフに反映。

## Chart.jsの読み込み
今回はCDNを利用してChart.jsのライブラリを読み込んでいます。

```
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
```

npmでインストールする場合は[公式サイト](https://misc.0o0o.org/chartjs-doc-ja/getting-started/installation.html
)を参考にしてみてください。

## コードの解説
今回作成したコードです。

### HTML
フォームを利用してユーザーが入力したデータを基にグラフを描画します。

```HTML:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>カスタム横棒グラフ</title>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
    <h1>カスタム横棒グラフ</h1>
    <form id="dataForm">
        <label for="label1">ラベル1:</label>
        <input type="text" id="label1" placeholder="ラベル1">
        <label for="value1">値1:</label>
        <input type="number" id="value1" placeholder="値1">
        <label for="label2">ラベル2:</label>
        <input type="text" id="label2" placeholder="ラベル2">
        <label for="value2">値2:</label>
        <input type="number" id="value2" placeholder="値2">
        <label for="label3">ラベル3:</label>
        <input type="text" id="label3" placeholder="ラベル3">
        <label for="value3">値3:</label>
        <input type="number" id="value3" placeholder="値3">
        <button type="button" id="drawButton">グラフを描画</button>
    </form>
    <canvas id="myChart" width="800" height="400"></canvas>
    <script src="script.js"></script>
</body>
</html>
```

### JavaScript
以下のスクリプトで、ユーザー入力を元に横棒グラフを描画します。

```JavaScript:script.js
let myChart;

document.getElementById("drawButton").addEventListener("click", function () {
  const label1 = document.getElementById("label1").value || "デフォルト1";
  const value1 = parseFloat(document.getElementById("value1").value) || 0;
  const label2 = document.getElementById("label2").value || "デフォルト2";
  const value2 = parseFloat(document.getElementById("value2").value) || 0;
  const label3 = document.getElementById("label3").value || "デフォルト3";
  const value3 = parseFloat(document.getElementById("value3").value) || 0;

  const labels = [label1, label2, label3];
  const data = [value1, value2, value3];

  const ctx = document.getElementById("myChart").getContext("2d");

  if (myChart instanceof Chart) {
    myChart.destroy();
  }

  myChart = new Chart(ctx, {
    type: "bar",
    data: {
      labels: labels,
      datasets: [
        {
          label: "データセット",
          data: data,
          backgroundColor: ["#b2cbe4", "#ccd7e3", "#ccd7e3"], // 背景色の設定
        },
      ],
    },
    options: {
      indexAxis: "y", // 横棒グラフ
      responsive: true,
      layout: {
        padding: {
          right: 50, // ラベル表示のために余白を追加
        },
      },
      plugins: {
        legend: {
          display: false, // 凡例を非表示
        },
        tooltip: {
          enabled: false, // ツールチップを非表示
        },
      },
      scales: {
        x: {
          beginAtZero: true,
          grid: {
            display: false, // 縦線を非表示
          },
        },
        y: {
          grid: {
            drawBorder: false, // Y軸の外枠を非表示（任意）
          },
        },
      },
    },
    plugins: [
      {
        id: "customDottedLines",
        afterDatasetsDraw(chart) {
          const { ctx, chartArea, scales } = chart;
          const dataset = chart.data.datasets[0];

          ctx.save();
          ctx.setLineDash([1, 2]); // 点線パターン: 1ピクセル線、2ピクセル間隔
          ctx.strokeStyle = "#000"; // 点線の色

          dataset.data.forEach((value, index) => {
            const yCenter = scales.y.getPixelForValue(index); // Y軸の中央位置を取得
            const xStart = scales.x.getPixelForValue(value); // バーの右端
            const xLabel = chartArea.right + 10; // ラベル位置（右側）

            // 点線を描画
            ctx.beginPath();
            ctx.moveTo(xStart, yCenter); // バーの右端中央からスタート
            ctx.lineTo(xLabel, yCenter); // ラベル位置中央で終了
            ctx.stroke();

            // ラベルを描画
            ctx.fillStyle = "#000"; // ラベルの色
            ctx.font = "12px Arial"; // ラベルのフォント
            ctx.fillText(value, xLabel + 5, yCenter + 4); // 数値ラベルを描画
          });

          ctx.restore();
        },
      },
    ],
  });
});
```

CSS
```css:styles.css
/* 全体のスタイル */
body {
  font-family: "Arial", sans-serif;
  margin: 0;
  padding: 0;
  background-color: #f9f9f9;
  color: #333;
  line-height: 1.6;
}

/* ヘッダーとフッター */
header,
footer {
  background: #333;
  color: #fff;
  text-align: center;
  padding: 15px 0;
  font-size: 1.2rem;
}

/* メインコンテンツ */
main {
  padding: 20px;
  max-width: 900px;
  margin: 0 auto;
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}

/* フォームのスタイル */
form {
  margin-bottom: 20px;
  padding: 20px;
  background: #f5f5f5;
  border-radius: 8px;
  border: 1px solid #ddd;
}

.form-group {
  margin-bottom: 15px;
  display: flex;
  flex-direction: column;
}

label {
  font-weight: bold;
  margin-bottom: 5px;
  font-size: 1rem;
}

input {
  padding: 10px;
  font-size: 1rem;
  border: 1px solid #ddd;
  border-radius: 5px;
  transition: border-color 0.3s;
  outline: none;
}

input:focus {
  border-color: #007bff;
}

button {
  padding: 12px 20px;
  font-size: 1rem;
  background-color: #007bff;
  color: #fff;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  transition: background-color 0.3s;
}

button:hover {
  background-color: #0056b3;
}

/* グラフのスタイル */
canvas {
  display: block;
  max-width: 100%;
  margin: 20px auto;
  background: #fff;
  border: 1px solid #ddd;
  border-radius: 5px;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}

/* レスポンシブ対応 */
@media (max-width: 768px) {
  form {
    padding: 15px;
  }

  button {
    font-size: 0.9rem;
  }

  label,
  input {
    font-size: 0.9rem;
  }
}
```

## 解説
### 横棒グラフの作成

- type: "bar" を指定し、indexAxis: "y" を使って横棒グラフを描画します。

### 点線の追加

- afterDatasetsDraw を使ってカスタムプラグインを作成し、バーの右端からラベルまで点線を描画。

### 数値ラベルの描画

- ctx.fillText を使い、点線の右端に数値を描画。
### 背景色の設定

- 各バーの背景色を個別に設定可能。

### 数値のフォーマット
下記の部分を↓のように変更することで数値のラベルのフォーマットを変更することができる。

```
ctx.fillText(value, xLabel + 5, yCenter + 4);
```
```
const formattedValue = (value / 10000).toFixed(1) + "万";
ctx.fillText(formattedValue, xLabel + 5, yCenter + 4);
```

## 参考情報
[Chart.js公式サイト](https://misc.0o0o.org/chartjs-doc-ja/charts/bar.html)