Vue.js で棒グラフと積み上げ棒グラフを作成

この記事では、Vue.js と Chart.js を組み合わせて、インタラクティブな棒グラフと積み上げ棒グラフを作成する方法を解説します。データの更新、クリックイベント処理について詳しく説明します。

### 1. プロジェクトの準備

まず、Vue.js プロジェクトを作成します。Vue CLI を使用すると簡単にプロジェクトをセットアップできます。

```bash
vue create chart-app
cd chart-app
```

プロジェクトを作成したら、Chart.js をインストールします。

```bash
npm install chart.js
```

### 2. テンプレートの作成

`src/App.vue` に以下のテンプレートを追加します。

```vue
<template>
  <div>
    <canvas id="barChart" @click="handleChartClick($event, barChart)"></canvas>
    <button @click="updateChartData('bar', barChart)">
      棒グラフのデータを更新
    </button>

    <canvas
      id="stackedBarChart"
      @click="handleChartClick($event, stackedBarChart)"
    ></canvas>
    <button @click="updateChartData('stackedBar', stackedBarChart)">
      積み上げグラフのデータを更新
    </button>
  </div>
</template>
```

このテンプレートには、棒グラフと積み上げ棒グラフを表示するための `<canvas>` 要素と、データ更新用のボタンが含まれています。

### 3. JavaScript の実装

`src/App.vue` に以下の JavaScript コードを追加します。

```vue
<script setup>
import { ref, onMounted } from "vue";
import {
  Chart,
  CategoryScale,
  BarController,
  BarElement,
  Title,
  Tooltip,
  LinearScale,
} from "chart.js";

// データ
const chartData = ref({
  labels: ["1月", "2月", "3月", "4月", "5月", "6月", "7月"],
  datasets: [
    {
      label: "売上 A",
      data: [],
      backgroundColor: "rgba(255, 99, 132, 0.2)",
      borderColor: "rgba(255, 99, 132, 1)",
      borderWidth: 1,
    },
    {
      label: "売上 B",
      data: [],
      backgroundColor: "rgba(54, 162, 235, 0.2)",
      borderColor: "rgba(54, 162, 235, 1)",
      borderWidth: 1,
    },
  ],
});

// オプション
const barOptions = {
  responsive: true,
  scales: {
    y: {
      beginAtZero: true,
    },
  },
};

const stackedBarOptions = {
  responsive: true,
  scales: {
    y: {
      beginAtZero: true,
      stacked: true, // 積み上げを有効にする
    },
    x: {
      stacked: true, // 積み上げを有効にする
    },
  },
};

// チャートのインスタンス
let barChart = null;
let stackedBarChart = null;

// ランダムなデータを生成する関数
const generateRandomData = () => {
  const newData = [];
  for (let i = 0; i < 7; i++) {
    newData.push(Math.floor(Math.random() * 100) + 1);
  }
  return newData;
};

// 色の配列を生成する関数
const generateRandomColors = (num) => {
  const colors = [];
  for (let i = 0; i < num; i++) {
    const r = Math.floor(Math.random() * 256);
    const g = Math.floor(Math.random() * 256);
    const b = Math.floor(Math.random() * 256);
    colors.push(`rgba(${r}, ${g}, ${b}, 0.2)`);
  }
  return colors;
};

// チャートデータを更新する関数
const updateChartData = (chartType, chart) => {
  if (chart) {
    if (chartType === "bar") {
      chart.data.datasets[0].data = generateRandomData();
      chart.data.datasets[0].backgroundColor = generateRandomColors(7);
      chart.data.datasets[0].borderColor = generateRandomColors(7).map(
        (color) => color.replace("0.2", "1")
      );
    } else if (chartType === "stackedBar") {
      chart.data.datasets.forEach((dataset) => {
        dataset.data = generateRandomData();
      });
    }
    chart.update();
  }
};

// チャートクリックイベントハンドラ
const handleChartClick = (event, chart) => {
  const points = chart.getElementsAtEventForMode(
    event,
    "nearest",
    { intersect: true },
    false
  );

  if (points.length) {
    const firstPoint = points[0];
    const label = chart.data.labels[firstPoint.index];
    const value =
      chart.data.datasets[firstPoint.datasetIndex].data[firstPoint.index];
    console.log(`クリックしたバーのデータ: ${label}: ${value}`);
  }
};

onMounted(() => {
  Chart.register(
    CategoryScale,
    BarController,
    BarElement,
    Title,
    Tooltip,
    LinearScale
  );

  const barCanvas = document.getElementById("barChart");
  barChart = new Chart(barCanvas, {
    type: "bar",
    data: chartData.value,
    options: barOptions,
  });

  const stackedBarCanvas = document.getElementById("stackedBarChart");
  stackedBarChart = new Chart(stackedBarCanvas, {
    type: "bar",
    data: chartData.value,
    options: stackedBarOptions,
  });

  // 初期データの生成
  chartData.value.datasets.forEach((dataset) => {
    dataset.data = generateRandomData();
  });
});
</script>

```

### 4. Chart.js の設定

`onMounted` ライフサイクルフックの中で、Chart.js を初期化します。

```javascript
onMounted(() => {
  Chart.register(
    CategoryScale,
    BarController,
    BarElement,
    Title,
    Tooltip,
    LinearScale
  );

  const barCanvas = document.getElementById("barChart");
  barChart = new Chart(barCanvas, {
    type: "bar",
    data: chartData.value,
    options: barOptions,
  });

  const stackedBarCanvas = document.getElementById("stackedBarChart");
  stackedBarChart = new Chart(stackedBarCanvas, {
    type: "bar",
    data: chartData.value,
    options: stackedBarOptions,
  });

  // 初期データの生成
  chartData.value.datasets.forEach((dataset) => {
    dataset.data = generateRandomData();
  });
});
```

このコードでは、Chart.js の必要なコンポーネントを登録し、`barChart` と `stackedBarChart` のインスタンスを作成しています。

### 5. データの更新とクリックイベント処理

`updateChartData` 関数は、棒グラフと積み上げ棒グラフのデータをランダムに更新します。`handleChartClick` 関数は、クリックされた棒グラフのラベルと値をコンソールに出力します。

```javascript
// チャートデータを更新する関数
const updateChartData = (chartType, chart) => {
  if (chart) {
    if (chartType === "bar") {
      chart.data.datasets[0].data = generateRandomData();
      chart.data.datasets[0].backgroundColor = generateRandomColors(7);
      chart.data.datasets[0].borderColor = generateRandomColors(7).map(
        (color) => color.replace("0.2", "1")
      );
    } else if (chartType === "stackedBar") {
      chart.data.datasets.forEach((dataset) => {
        dataset.data = generateRandomData();
      });
    }
    chart.update();
  }
};

// チャートクリックイベントハンドラ
const handleChartClick = (event, chart) => {
  const points = chart.getElementsAtEventForMode(
    event,
    "nearest",
    { intersect: true },
    false
  );

  if (points.length) {
    const firstPoint = points[0];
    const label = chart.data.labels[firstPoint.index];
    const value =
      chart.data.datasets[firstPoint.datasetIndex].data[firstPoint.index];
    console.log(`クリックしたバーのデータ: ${label}: ${value}`);
  }
};
```

### 6. 実行

`npm run serve` コマンドを実行して、開発サーバーを起動します。ブラウザで http://localhost:8080/ にアクセスすると、棒グラフと積み上げ棒グラフが表示されます。ボタンをクリックすると、グラフのデータが更新されます。また、グラフのバーをクリックすると、コンソールにクリックされたバーのデータが表示されます。

### まとめ

この記事では、Vue.js と Chart.js を組み合わせて、インタラクティブな棒グラフと積み上げ棒グラフを作成する方法を説明しました。
また、今回はサンプルとして1つのファイルで作成していますがグラフごとにコンポーネント分けや共通処理を別ファイルに分けることによってコードの可読性は向上すると思いますのであくまでサンプルとして使用してみてください。