Astroの静的サイトにChart.jsのグラフを表示したい

皆さん、こんにちは。技術開発グループのn-ozawanです。
ディスプレイを新調しました。以前のディスプレイは横線が入ってソースコードが読み辛かったのですが、今はばっちり見えます。

本題です。
現在、当社ではとあるシステムを内製しており、Markdownで記述した設計書を見やすくするためにAstroで静的サイトを構築しています。詳しくは以前の投稿をご確認ください。その内製も今はテストフェーズに入りました。テストの状況はテスト消化曲線と不具合発生曲線で把握したいものです。今回はChart.jsでグラフを描画して、Astroの静的サイトに表示する方法を紹介します。

Chart.js

概要

Chart.jsは簡単にグラフを描画出来る、JavaScriptのライブラリです。Nodeなどの環境が無くても動作します。例えば以下のHTMLファイルを用意してブラウザで開くと棒グラフが表示されます。

<html>
<head></head>
<body>
  <div style="width: 80%">
    <canvas id="myChart"></canvas>
  </div>

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

  <script>
  const ctx = document.getElementById('myChart');

  new Chart(ctx, {
    type: 'bar',
    data: {
      labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
      datasets: [{
        label: '# of Votes',
        data: [12, 19, 3, 5, 2, 3],
        borderWidth: 1
      }]
    },
    options: {
    scales: {
        y: {
          beginAtZero: true
        }
      }
    }
  });
  </script>
</body>
</html>

折れ線グラフを描く

上記のサンプルは棒グラフでした。次はソースコードを1つ1つ追いながら、実際に折れ線グラフを表示してみましょう。まず最初はchart.jsのライブラリを読み込むところからです。

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

chart.jsを読み込んでいます。特に説明は不要かと思います。

  <div style="width: 80%">
    <canvas id="myChart"></canvas>
  </div>

  <!-- 省略 -->

  const ctx = document.getElementById('myChart');

グラフを描画する要素のコンテキストを取得しています。上記はid="myChart"のcanvas要素にグラフを描画します。

  new Chart(ctx, {
    type: 'line',
    data: {
      labels: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
      datasets: [
        {
          label: 'Dataset 1',
          data: [89, 55, 40, 42, 46, 6, 27],
        },
        {
          label: 'Dataset 2',
          data: [0, 47, 73, 37, 85, 76],
        },
        {
          label: 'Dataset 3',
          data: [56, 98, 23, 27, 32, 53, 67, 34],
        }
      ]
    },
    options: {},
  });

new Chart(...)によりグラフを描画します。第1引数には先ほど取得した要素のコンテキストを指定します。第2引数には描画するグラフの情報を指定します。

data.labelsは横軸です。サンプルでは土曜から日曜までの曜日を指定しています。

data.datasetsは実際に描画するデータを指定します。data.datasets.dataの内容によって縦軸が自動的に調整されます。また、data.datasets.dataには配列を指定しますが、その配列の要素数はdata.labelsと同じか、それ以下にする必要があります。もし、data.labelsの要素数以上を指定した場合、超過分は描画されません。

全体のソースコードは以下の通りです。

<html>
<head></head>
<body>
  <div style="width: 80%">
    <canvas id="myChart"></canvas>
  </div>

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

  <script>
  const ctx = document.getElementById('myChart');

  new Chart(ctx, {
    type: 'line',
    data: {
      labels: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
      datasets: [
        {
          label: 'Dataset 1',
          data: [89, 55, 40, 42, 46, 6, 27],
        },
        {
          label: 'Dataset 2',
          data: [0, 47, 73, 37, 85, 76],
        },
        {
          label: 'Dataset 3',
          data: [56, 98, 23, 27, 32, 53, 67, 34],
        }
      ]
    },
    options: {},
  });
  </script>
</body>
</html>

対応のグラフいろいろ

Chart.jsは棒グラフと折れ線グラフ以外にも対応しています。以下にいくつか紹介します。画像をクリックすると公式サイトのサンプルページに飛びます。

棒グラフと折れ線グラフ

バブルチャート

パイチャート

レーダーチャート

Astroで描画する方法

Astroでグラフを描画するには、ちょっとしたコツが必要になります。なぜなら、グラフを描画するタイミングが、ビルドするときではなく、静的HTMLを画面に表示するときだからです。グラフ描画に必要なデータを、静的HTMLに埋め込む必要があります。

静的HTMLにデータを埋め込むには、HTMLのカスタム要素を使います。

<chart-provider data-chartdata={JSON.stringify(progressChartData)}>
  <h3>グラフ</h3>
  <canvas/>
</chart-provider>

<script>
  import { Chart, registerables, type ChartData } from "chart.js"
  Chart.register(...registerables);

  class ChartProvider extends HTMLElement {
    constructor() {
      super();

      const ctx = this.getElementsByTagName("canvas");
      const chartData: ChartData = JSON.parse(this.dataset.chartdata ?? "{}");

      new Chart(ctx, {
        type: 'line',
        data: Object.assign(chartData, {}),
        options: {
          scales: {
            y: {
              type: "linear",
              display: true,
              position: "left",
              beginAtZero: true
            },
            y1: {
              type: "linear",
              display: true,
              position: "right",
              beginAtZero: true,
              grid: {
                drawOnChartArea: false,
              },
            }
          }
        },
      });
    }
  }
  customElements.define("chart-provider", ChartProvider)
</script>

chart-providerはカスタム要素です。属性としてdata-chartdataを受け取ります。カスタム要素は42行目にあるcustomElements.define(...)で定義します。第2引数にはHTMLElementを継承したクラスを指定します。

ChartProviderの中身はシンプルです。this.dataset.chartdataで属性data-chartdataの内容が取得できます。あとは、通常のChart.jsでグラフを描画するだけです。

おわりに

簡単にグラフが描画出来るのっていいですね。内製プロジェクトでは以下のようにテスト状況を表現しており、関係者がブラウザから気軽に確認出来るようにしています。

ではまた。

Recommendおすすめブログ