# ステップバイステップガイド

Chart.js の主要な概念(グラフの種類と要素、データセット、カスタマイズ、プラグイン、コンポーネント、ツリーシェイキング)を理解するために、このガイドに従ってください。テキスト内のリンクをたどることもためらわないでください。

Chart.js を使用して、いくつかのグラフを含むデータ可視化をゼロから構築します。

result

# Chart.js で新しいアプリケーションを構築する

新しいフォルダーに、次の内容を含む package.json ファイルを作成します。

{
  "name": "chartjs-example",
  "version": "1.0.0",
  "license": "MIT",
  "scripts": {
    "dev": "parcel src/index.html",
    "build": "parcel build src/index.html"
  },
  "devDependencies": {
    "parcel": "^2.6.2"
  },
  "dependencies": {
    "@cubejs-client/core": "^0.31.0",
    "chart.js": "^4.0.0"
  }
}

最新のフロントエンドアプリケーションでは、JavaScript モジュールバンドラーがよく使用されるため、ゼロ構成の便利なビルドツールとして Parcel (新しいウィンドウで開きます) を選択しました。Chart.js v4 と、Cube (新しいウィンドウで開きます) 用の JavaScript クライアントもインストールしています。これはデータアプリ用のオープンソース API で、実世界のデータを取得するために使用します(詳細については後述)。

npm installyarn install、または pnpm install を実行して依存関係をインストールし、次に src フォルダーを作成します。そのフォルダーの中に、非常にシンプルな index.html ファイルが必要です。

<!doctype html>
<html lang="en">
  <head>
    <title>Chart.js example</title>
  </head>
  <body>
    <!-- <div style="width: 500px;"><canvas id="dimensions"></canvas></div><br/> -->
    <div style="width: 800px;"><canvas id="acquisitions"></canvas></div>
    <!-- <script type="module" src="dimensions.js"></script> -->
    <script type="module" src="acquisitions.js"></script>
  </body>
</html>

ご覧のとおり、Chart.js には最小限のマークアップが必要です。後でグラフを参照する id を持つ canvas タグです。デフォルトでは、Chart.js のグラフは レスポンシブで、囲んでいるコンテナ全体を使用します。そのため、div の幅を設定してグラフの幅を制御します。

最後に、次の内容で src/acquisitions.js ファイルを作成します。

import Chart from 'chart.js/auto'
(async function() {
  const data = [
    { year: 2010, count: 10 },
    { year: 2011, count: 20 },
    { year: 2012, count: 15 },
    { year: 2013, count: 25 },
    { year: 2014, count: 22 },
    { year: 2015, count: 30 },
    { year: 2016, count: 28 },
  ];
  new Chart(
    document.getElementById('acquisitions'),
    {
      type: 'bar',
      data: {
        labels: data.map(row => row.year),
        datasets: [
          {
            label: 'Acquisitions by year',
            data: data.map(row => row.count)
          }
        ]
      }
    }
  );
})();

このコードを詳しく見ていきましょう。

  • 特別な chart.js/auto パスから、メインの Chart.js クラスである Chart をインポートします。これにより、利用可能なすべての Chart.js コンポーネントがロードされ(非常に便利です)、ツリーシェイキングは禁止されます。これについては後で説明します。
  • 新しい Chart インスタンスをインスタンス化し、グラフがレンダリングされるキャンバス要素とオプションオブジェクトの 2 つの引数を指定します。
  • グラフの種類(bar)を指定し、data を指定する必要があります。datalabels(多くの場合、データポイントの数値またはテキストによる説明)と datasets の配列で構成されます(Chart.js はほとんどのグラフの種類で複数のデータセットをサポートします)。各データセットには label が指定され、データポイントの配列が含まれています。
  • 今のところ、ダミーデータの入力がいくつかしかありません。そのため、year プロパティと count プロパティを抽出して、唯一のデータセット内の labels とデータポイントの配列を作成します。

npm run devyarn dev、または pnpm dev を使用して例を実行し、Web ブラウザーで localhost:1234 (新しいウィンドウで開きます) に移動します。

result

わずか数行のコードで、凡例グリッド線目盛り、およびホバー時に表示されるツールチップなどの多くの機能を持つグラフが作成されました。Web ページを数回更新して、グラフもアニメーション化されていることを確認してください。「年別買収」ラベルをクリックすると、データセットの表示を切り替えることもできます(特に、複数のデータセットがある場合に便利です)。

# 簡単なカスタマイズ

Chart.js のグラフをカスタマイズする方法を見てみましょう。まず、アニメーションをオフにして、グラフがすぐに表示されるようにします。次に、データセットが 1 つしかなく、データが非常に単純なため、凡例とツールチップを非表示にします。

src/acquisitions.js 内の new Chart(...); の呼び出しを次のスニペットに置き換えます。

  new Chart(
    document.getElementById('acquisitions'),
    {
      type: 'bar',
      options: {
        animation: false,
        plugins: {
          legend: {
            display: false
          },
          tooltip: {
            enabled: false
          }
        }
      },
      data: {
        labels: data.map(row => row.year),
        datasets: [
          {
            label: 'Acquisitions by year',
            data: data.map(row => row.count)
          }
        ]
      }
    }
  );

ご覧のとおり、2 番目の引数に options プロパティを追加しました。これが、Chart.js のあらゆる種類のカスタマイズオプションを指定する方法です。animation を介してブールフラグを指定すると、アニメーションが無効になります。ほとんどのグラフ全体のオプション(たとえば、レスポンシブデバイスピクセル比)は、このように構成されます。

凡例とツールチップは、plugins のそれぞれのセクションの下でブールフラグを指定すると非表示になります。Chart.js の機能の一部は、自己完結型の別個のコードであるプラグインに抽出されていることに注意してください。そのうちのいくつかは、Chart.js ディストリビューション (新しいウィンドウで開きます)の一部として利用でき、その他のプラグインは独立して管理され、プラグイン、フレームワーク統合、および追加のグラフタイプの素晴らしいリスト (新しいウィンドウで開きます)にあります。

ブラウザーで更新された最小限のグラフが表示されるはずです。

# 実世界のデータ

ハードコードされた、サイズが限られた、現実的でないデータでは、Chart.js の可能性を十分に発揮することは困難です。データ API にすばやく接続して、サンプルアプリケーションを実際のユースケースに近づけましょう。

次の内容で src/api.js ファイルを作成します。

import { CubejsApi } from '@cubejs-client/core';
const apiUrl = 'https://heavy-lansford.gcp-us-central1.cubecloudapp.dev/cubejs-api/v1';
const cubeToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjEwMDAwMDAwMDAsImV4cCI6NTAwMDAwMDAwMH0.OHZOpOBVKr-sCwn8sbZ5UFsqI3uCs6e4omT7P6WVMFw';
const cubeApi = new CubejsApi(cubeToken, { apiUrl });
export async function getAquisitionsByYear() {
  const acquisitionsByYearQuery = {
    dimensions: [
      'Artworks.yearAcquired',
    ],
    measures: [
      'Artworks.count'
    ],
    filters: [ {
      member: 'Artworks.yearAcquired',
      operator: 'set'
    } ],
    order: {
      'Artworks.yearAcquired': 'asc'
    }
  };
  const resultSet = await cubeApi.load(acquisitionsByYearQuery);
  return resultSet.tablePivot().map(row => ({
    year: parseInt(row['Artworks.yearAcquired']),
    count: parseInt(row['Artworks.count'])
  }));
}
export async function getDimensions() {
  const dimensionsQuery = {
    dimensions: [
      'Artworks.widthCm',
      'Artworks.heightCm'
    ],
    measures: [
      'Artworks.count'
    ],
    filters: [
      {
        member: 'Artworks.classification',
        operator: 'equals',
        values: [ 'Painting' ]
      },
      {
        member: 'Artworks.widthCm',
        operator: 'set'
      },
      {
        member: 'Artworks.widthCm',
        operator: 'lt',
        values: [ '500' ]
      },
      {
        member: 'Artworks.heightCm',
        operator: 'set'
      },
      {
        member: 'Artworks.heightCm',
        operator: 'lt',
        values: [ '500' ]
      }
    ]
  };
  const resultSet = await cubeApi.load(dimensionsQuery);
  return resultSet.tablePivot().map(row => ({
    width: parseInt(row['Artworks.widthCm']),
    height: parseInt(row['Artworks.heightCm']),
    count: parseInt(row['Artworks.count'])
  }));
}

そこで何が起こっているのか見てみましょう。

  • データアプリ用のオープンソース API である Cube (新しいウィンドウで開きます) の JavaScript クライアントライブラリを import し、API URL(apiUrl)と認証トークン(cubeToken)で構成し、最後にクライアント(cubeApi)をインスタンス化します。
  • Cube API はCube Cloud (新しいウィンドウで開きます) でホストされ、ニューヨーク(アメリカ)の近代美術館 (新しいウィンドウで開きます)のコレクション内のすべての芸術作品を表す約 140,000 件のレコードを含む公開データセット (新しいウィンドウで開きます) に接続されたデータベースに接続されています。確かに、現在よりも現実的なデータセットです。
  • API からデータを取得するために、いくつかの非同期関数(getAquisitionsByYeargetDimensions)を定義します。1 つ目は買収年ごとのアートワークの数を返し、もう 1 つはすべての幅と高さのペアのアートワークの数を返します(別のグラフに必要になります)。
  • getAquisitionsByYear を見てみましょう。最初に、acquisitionsByYearQuery 変数に宣言型の JSON ベースのクエリを作成します。ご覧のとおり、すべての yearAcquired に対して、アートワークの count を取得したいことを指定します。yearAcquired を設定する必要があります(つまり、未定義ではありません)。結果セットは yearAcquired で昇順にソートされます。
  • 次に、cubeApi.loadを呼び出してresultSetを取得し、目的のyearcountプロパティを持つオブジェクトの配列にマッピングします。

それでは、実世界のデータをチャートに表示しましょう。src/acquisitions.jsにいくつかの変更を加えてください。インポートを追加し、data変数の定義を置き換えます。

import { getAquisitionsByYear } from './api'
// ...
const data = await getAquisitionsByYear();

完了しました!これで、実世界のデータを使用したチャートは次のようになります。1964年、1968年、2008年に何か面白いことが起こったようですね!

result

棒グラフはこれで完了です。別のChart.jsのチャートタイプを試してみましょう。

# さらなるカスタマイズ

Chart.jsは、多くの一般的なチャートタイプをサポートしています。

例えば、バブルチャートでは、3次元のデータを同時に表示できます。x軸とy軸上の位置が2次元を表し、3次元目は個々のバブルのサイズで表されます。

チャートを作成するには、既に実行中のアプリケーションを停止し、src/index.htmlに移動して、次の2行のコメントを解除します。

<div style="width: 500px;"><canvas id="dimensions"></canvas></div><br/>
<script type="module" src="dimensions.js"></script>

次に、次の内容でsrc/dimensions.jsファイルを作成します。

import Chart from 'chart.js/auto'
import { getDimensions } from './api'
(async function() {
  const data = await getDimensions();
  new Chart(
    document.getElementById('dimensions'),
    {
      type: 'bubble',
      data: {
        labels: data.map(x => x.year),
        datasets: [
          {
            label: 'Dimensions',
            data: data.map(row => ({
              x: row.width,
              y: row.height,
              r: row.count
            }))
          }
        ]
      }
    }
  );
})();

おそらく、すべてが非常に簡単でしょう。APIからデータを取得し、bubbleタイプの新しいチャートをレンダリングし、3次元のデータをxy、およびr(半径)プロパティとして渡します。

次に、rm -rf .parcel-cacheでキャッシュをリセットし、npm run devyarn dev、またはpnpm devでアプリケーションを再度起動します。新しいチャートを確認できるようになりました。

result

うーん、あまり見栄えが良くありませんね。

まず第一に、チャートが正方形ではありません。アートワークの幅と高さは等しく重要なので、チャートの幅を高さと同じにしたいと思います。デフォルトでは、Chart.jsチャートのアスペクト比は、1(すべての円形チャート、例えばドーナツチャート)または2(残りのすべて)のいずれかです。チャートのアスペクト比を変更してみましょう。

// ...
	new Chart(
    document.getElementById('dimensions'),
    {
      type: 'bubble',
      options: {
        aspectRatio: 1,
      },
// ...

ずいぶん良くなりました。

result

しかし、まだ理想的ではありません。横軸は0から500まで、縦軸は0から450まで伸びています。デフォルトでは、Chart.jsは、データセットで提供された値に合わせて、軸の範囲(最小値と最大値)を自動的に調整するため、チャートはデータに「適合」します。どうやら、MoMaのコレクションには、高さが450〜500 cmの範囲のアートワークはないようです。チャートの軸設定を変更して、それを考慮してみましょう。

// ...
  new Chart(
    document.getElementById('dimensions'),
    {
      type: 'bubble',
      options: {
        aspectRatio: 1,
        scales: {
          x: {
            max: 500
          },
          y: {
            max: 500
          }
        }
      },
// ...

素晴らしい!更新されたチャートをご覧ください。

result

しかし、もう一つ気になる点があります。これらの数字は何ですか?単位がセンチメートルであることはあまり明確ではありません。両方の軸にカスタムティックフォーマットを適用して、明確にしましょう。各ティック値をフォーマットするために呼び出されるコールバック関数を提供します。更新された軸設定は次のとおりです。

// ...
  new Chart(
    document.getElementById('dimensions'),
    {
      type: 'bubble',
      options: {
        aspectRatio: 1,
        scales: {
          x: {
            max: 500,
            ticks: {
              callback: value => `${value / 100} m`
            }
          },
          y: {
            max: 500,
            ticks: {
              callback: value => `${value / 100} m`
            }
          }
        }
      },
// ...

完璧です。これで両方の軸に適切な単位が表示されました。

result

# 複数のデータセット

Chart.jsは、各データセットを独立してプロットし、カスタムスタイルを適用できます。

チャートを見てください。正方形のアートワークを表す、同じx座標とy座標を持つバブルの目に見える「線」があります。これらのバブルを独自のデータセットに入れて、異なる色でペイントすると良いでしょう。また、「背の高い」アートワークと「幅の広い」アートワークを分離して、それぞれ異なる色でペイントすることもできます。

これはその方法です。datasetsを次のコードに置き換えます。

// ...
        datasets: [
          {
            label: 'width = height',
            data: data
              .filter(row => row.width === row.height)
              .map(row => ({
                x: row.width,
                y: row.height,
                r: row.count
              }))
          },
          {
            label: 'width > height',
            data: data
              .filter(row => row.width > row.height)
              .map(row => ({
                x: row.width,
                y: row.height,
                r: row.count
              }))
          },
          {
            label: 'width < height',
            data: data
              .filter(row => row.width < row.height)
              .map(row => ({
                x: row.width,
                y: row.height,
                r: row.count
              }))
          }
        ]
// ..

ご覧のとおり、異なるラベルを持つ3つのデータセットを定義しています。各データセットは、filterで抽出された独自のデータスライスを取得します。これで、視覚的に区別できるようになり、既にご存じのように、表示/非表示を個別に切り替えることができます。

result

ここでは、デフォルトのカラーパレットを使用しています。ただし、すべてのチャートタイプが、自由にカスタマイズできる多くのデータセットオプションをサポートしていることを忘れないでください。

# プラグイン

Chart.jsチャートをカスタマイズするもう1つの(非常に強力な!)方法は、プラグインを使用することです。プラグインディレクトリ (新しいウィンドウで開きます)でいくつか見つけたり、アドホックなものを作成したりできます。Chart.jsのエコシステムでは、プラグインでチャートを微調整することが慣例であり、期待されています。例えば、シンプルなアドホックプラグインで、キャンバスの背景をカスタマイズしたり、境界線を追加したりできます。後者を試してみましょう。

プラグインには豊富なAPIがありますが、要するに、プラグインはnameと、拡張ポイントで定義された1つ以上のコールバック関数を持つオブジェクトとして定義されます。src/dimensions.jsnew Chart(...);呼び出しの前とその場所に、次のスニペットを挿入します。

// ...
  const chartAreaBorder = {
    id: 'chartAreaBorder',
    beforeDraw(chart, args, options) {
      const { ctx, chartArea: { left, top, width, height } } = chart;
      ctx.save();
      ctx.strokeStyle = options.borderColor;
      ctx.lineWidth = options.borderWidth;
      ctx.setLineDash(options.borderDash || []);
      ctx.lineDashOffset = options.borderDashOffset;
      ctx.strokeRect(left, top, width, height);
      ctx.restore();
    }
  };
  new Chart(
    document.getElementById('dimensions'),
    {
      type: 'bubble',
      plugins: [ chartAreaBorder ],
      options: {
        plugins: {
          chartAreaBorder: {
            borderColor: 'red',
            borderWidth: 2,
            borderDash: [ 5, 5 ],
            borderDashOffset: 2,
          }
        },
        aspectRatio: 1,
// ...

ご覧のとおり、このchartAreaBorderプラグインでは、キャンバスコンテキストを取得し、現在の状態を保存し、スタイルを適用し、チャート領域の周りに長方形を描画し、キャンバスの状態を復元します。また、pluginsでプラグインを渡すことで、この特定のチャートにのみ適用されます。また、プラグインオプションをoptions.plugins.chartAreaBorderで渡します。プラグインのソースコードでハードコーディングすることもできますが、この方がはるかに再利用可能です。

これでバブルチャートがより華やかになりました。

result

# ツリーシェイキング

本番環境では、エンドユーザーがデータアプリケーションをより速くロードし、より良いエクスペリエンスを得られるように、可能な限り少ないコードを配布するよう努めます。そのためには、JavaScriptバンドルから未使用のコードを削除するという意味のツリーシェイキング (新しいウィンドウで開きます)を適用する必要があります。

Chart.jsは、そのコンポーネント設計により、ツリーシェイキングを完全にサポートしています。すべてのChart.jsコンポーネントを一度に登録し(プロトタイプ作成時に便利です)、アプリケーションと一緒にバンドルできます。または、必要なコンポーネントのみを登録して、サイズの小さい最小限のバンドルを取得できます。

サンプルアプリケーションを調べてみましょう。バンドルサイズはどれくらいですか?アプリケーションを停止して、npm run buildyarn build、またはpnpm buildを実行できます。しばらくすると、次のようなものが表示されます。

% yarn build
yarn run v1.22.17
$ parcel build src/index.html
✨ Built in 88ms
dist/index.html              381 B   164ms
dist/index.74a47636.js   265.48 KB   1.25s
dist/index.ba0c2e17.js       881 B    63ms
✨ Done in 0.51s.

Chart.jsとその他の依存関係が、単一の265 KBファイルにまとめられていることがわかります。

バンドルサイズを削減するには、src/acquisitions.jssrc/dimensions.jsにいくつかの変更を適用する必要があります。まず、両方のファイルから次のインポートステートメントを削除する必要があります。import Chart from 'chart.js/auto'

代わりに、必要なコンポーネントのみをロードし、Chart.register(...)を使用してChart.jsに「登録」しましょう。src/acquisitions.jsで必要なのは次のとおりです。

import {
  Chart,
  Colors,
  BarController,
  CategoryScale,
  LinearScale,
  BarElement,
  Legend
} from 'chart.js'
Chart.register(
  Colors,
  BarController,
  BarElement,
  CategoryScale,
  LinearScale,
  Legend
);

そして、これがsrc/dimensions.jsのスニペットです。

import {
  Chart,
  Colors,
  BubbleController,
  CategoryScale,
  LinearScale,
  PointElement,
  Legend
} from 'chart.js'
Chart.register(
  Colors,
  BubbleController,
  PointElement,
  CategoryScale,
  LinearScale,
  Legend
);

Chartクラスに加えて、チャートタイプのコントローラー、スケール、およびその他のチャート要素(例えば、バーまたはポイント)もロードしていることがわかります。利用可能なすべてのコンポーネントは、ドキュメントで参照できます。

または、コンソールでChart.jsのアドバイスに従うこともできます。例えば、棒グラフのBarControllerのインポートを忘れた場合、ブラウザコンソールに次のメッセージが表示されます。

Unhandled Promise Rejection: Error: "bar" is not a registered controller.

アプリケーションを本番環境用に準備するときは、chart.js/autoからのインポートを注意深く確認することを忘れないでください。このようなインポートが1つだけあると、ツリーシェイキングが実質的に無効になります。

それでは、アプリケーションをもう一度調べてみましょう。yarn buildを実行すると、次のようなものが表示されます。

% yarn build
yarn run v1.22.17
$ parcel build src/index.html
✨ Built in 88ms
dist/index.html              381 B   176ms
dist/index.5888047.js    208.66 KB   1.23s
dist/index.dcb2e865.js       932 B    58ms
✨ Done in 0.51s.

選択したコンポーネントのみをインポートして登録することで、不要なコードを56 KB以上削除しました。他の依存関係がバンドルで約50 KBを占めることを考えると、ツリーシェイキングは、サンプルアプリケーションのバンドルからChart.jsコードの約25%を削除するのに役立ちます。

# 次のステップ

これで、Chart.jsの主要な概念(チャートタイプと要素、データセット、カスタマイズ、プラグイン、コンポーネント、ツリーシェイキング)に精通しました。

ドキュメントで多くのチャートの例を確認したり、Chart.jsプラグインと追加のチャートタイプの素晴らしいリスト (新しいウィンドウで開きます)、およびフレームワークの統合 (新しいウィンドウで開きます)(例えば、React、Vue、Svelteなど)を確認してください。また、Chart.js Discord (新しいウィンドウで開きます)に参加したり、TwitterでChart.jsをフォロー (新しいウィンドウで開きます)してください。

Chart.jsを使って楽しく、そして幸運を祈ります!

最終更新日: 2024/05/17 12:33:38 PM