Reactアプリで Pie chart (円グラフ)を表示したくなったので調べたところ、以下の記事がありました。
上記では色々なライブラリが紹介されていましたが、今回はWebに情報の多そうな Chart.js
を使ってみることにしました。
Chart.js | Open source HTML5 Charts for your website
次に、Chart.jsをReactで扱う方法を調べたところ、 react-chartjs-2
がありました。
react-chartjs-2 | react-chartjs-2
そこで、 Chart.js
+ react-chartjs-2
を使って React で Pie chart を表示してみたことから、メモを残します。
目次
- 環境
- ViteでReactアプリを作る
- react-chartjs-2 の環境を構築する
- Tanstack Router によるファイルベースルーティングの設定を追加する
- Chart.js で Pie chart を作成する
- Pie Chartの上にTooltipを常に表示する
- ソースコード
環境
- React 18.2.0
- Chart.js 4.4.2
- react-chartjs-2 5.2.0
- Tanstack Router 1.29.2
ViteでReactアプリを作る
Vite公式ドキュメントに従い、ViteでReactアプリを作ります。
Getting Started | Vite
$ npm create vite@latest ✔ Project name: … react_chart_example ✔ Select a framework: › React ✔ Select a variant: › TypeScript
react-chartjs-2 の環境を構築する
リポジトリのREADMEに従い、 chart.js react-chartjs-2
をインストールします。
$ npm install --save chart.js react-chartjs-2
Tanstack Router によるファイルベースルーティングの設定を追加する
今回はいくつかサンプルコードを作るので、ルーティングライブラリを入れておきます。
ただ、パスを考えるのが手間なので、ファイルベースルーティングができるライブラリを使いたくなりました。
以前は Generouted
+ React Router
を使っていました。
ファイルベースルーティング: Generouted + React Router | Reactにて、useStateやuseEffectを使っていたところをTanstack Queryに置き換えてみた - メモ的な思考的な
そんな中、 Tanstack Routerでもファイルベースルーティングができるようになったと知りました。
- TanStack Router(& Query)はSPA開発で求めていたものだった✨【Reactのルーティングとデータ取得】
- TanStack Router SPA開発の選択肢になるか(+TanStack Query) - Speaker Deck
そこで今回は、Tanstack Routerのファイルベースルーティングを試してみることにしました。
TanStack Router Docs
Tanstack Router のインストール
Tanstack Routerのドキュメントに従い、tanstack router と viteのプラグインをインストールします。
Vite Plugin | TanStack Router Docs
$ npm install @tanstack/react-router @tanstack/router-vite-plugin
devtools もインストールしておきます。
Devtools | TanStack Router Docs
$ npm install @tanstack/router-devtools --save
vite.config.ts への追加
公式ドキュメントに従い、 vite.config.ts
へ設定を追加します。
https://tanstack.com/router/latest/docs/framework/react/guide/file-based-routing#vite-configuration
import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import {TanStackRouterVite} from "@tanstack/router-vite-plugin"; // https://vitejs.dev/config/ export default defineConfig({ plugins: [ react(), TanStackRouterVite() // 追加 ], })
続いて、 npm run dev
し、 routeTree.gen.ts
を生成しておきます。
♻️ Generating routes... ✅ Processed route in 148ms
main.tsx の修正
Tanstack Router を組み込むため、 main.tsx を修正します。
import React from 'react' import ReactDOM from 'react-dom/client' import './index.css' import {createRouter, RouterProvider} from "@tanstack/react-router" import {routeTree} from "./routeTree.gen" const router = createRouter({ routeTree }) declare module '@tanstack/react-router' { interface Register { router: typeof router } } ReactDOM.createRoot(document.getElementById('root')!).render( <React.StrictMode> <RouterProvider router={router} /> </React.StrictMode>, )
__root.tsx の追加
ルートとなるファイル __root.tsx
を作成します。
import {createRootRoute, Outlet} from "@tanstack/react-router"; import {TanStackRouterDevtools} from "@tanstack/router-devtools"; export const Route = createRootRoute({ component: () => ( <> <Outlet /> <TanStackRouterDevtools /> </> ) })
以上で Tanstack Router の準備ができました。
Chart.js で Pie chart を作成する
はじめての Pie chart
Chart.js の exampleを参考に、Pie chart を作ります。
また、ファイルベースルーティングできるよう、Pie chartのコンポーネントとルーティングを src/routes/pie_chart/first_pie_chart.lazy.tsx
に作ります。
import {createLazyRoute} from "@tanstack/react-router" import {ArcElement, Chart as ChartJS, Legend, Tooltip} from 'chart.js' import {Pie} from 'react-chartjs-2' const Component = () => { ChartJS.register(ArcElement, Tooltip, Legend) const data = { labels: ['奥州ロマン', 'シナノゴールド', 'ピンクレディ', 'ブラムリー'], datasets: [ { label: '購入数', data: [1, 5, 3, 2], backgroundColor: [ 'firebrick', 'gold', 'pink', 'mediumseagreen' ], borderColor: [ 'firebrick', 'gold', 'pink', 'mediumseagreen' ], borderWidth: 1 } ] } return ( <Pie data={data} /> ) } export const Route = createLazyRoute('/pie_chart/first_pie_chart')({ component: Component })
実装ができたので、 npm run dev
で起動します。
その後、 http://localhost:5173/pie_chart/first_pie_chart
へアクセスすると、Pie chartが表示されました。
Legend (凡例) をカスタマイズする
今回は Legend の表示位置を Pie chart の右側に表示するようカスタマイズします。
カスタマイズ方法を調べたところ、 Chart.js のドキュメントの Legend
に情報がありました。
Legend | Chart.js
また、ドキュメントには Warning として
The doughnut, pie, and polar area charts override the legend defaults. To change the overrides for those chart types, the options are defined in
Chart.overrides[type].plugins.legend
.
と書かれていました。
そこで、
Chart.overrides.pie.plugins.legend
を指定- 表示位置のオプション
position
にright
を指定 - ファイル名は
pie_chart_with_legend_on_the_right.lazy.tsx
として実装しました。
const Component = () => { ChartJS.register(ArcElement, Tooltip, Legend) // 以下を追加 ChartJS.overrides.pie.plugins.legend.position = 'right' // ... // 他は同じ }
実装が終わったので、ブラウザで http://localhost:5173/pie_chart/pie_chart_with_legend_on_the_right
を開くと、Legend が右側に表示されました。
Pie chartの上にラベルをを常に表示する
今まで見てきた通り、Chart.js の場合、デフォルトではマイスオーバーすることで、各領域の内訳が表示されます。
ただ、マウスオーバーしなくてもPie chart上に表示する方法がないかを調べたところ、以下の記事がありました。
【Vue.js】Vue.js + Chart.js ドーナツグラフのちょっとした小技【vue-chart.js】| blog(スワブロ) | スワローインキュベート
これを参考に、 pie_chart_with_always_text.lazy.tsx
として実装してみました。
実装したときのメモは以下です。
alwaysTooltipPlugin
という名前で Chart.js のプラグインを作る- その中の
afterDraw
コールバックで、各領域の上にテキストを描画する
- その中の
ChartJS.register()
の引数に、追加したプラグインalwaysTooltipPlugin
を指定する
import {ArcElement, Chart as ChartJS, Legend, Tooltip} from "chart.js"; import {Pie} from "react-chartjs-2"; import {createLazyRoute} from "@tanstack/react-router"; const alwaysTooltipPlugin = { id: 'alwaysShowTooltip', afterDraw(chart: ChartJS) { const {ctx} = chart chart.data.datasets.forEach((_dataset, i) => { chart.getDatasetMeta(i).data.forEach((datapoint, index) => { const {x, y} = datapoint.tooltipPosition(false) const text = chart.data.labels ? (chart.data.labels[index] ?? '' + ': ' + chart.data.datasets[i].data[index]).toString() : '' ctx.textAlign = 'center' ctx.textBaseline = 'middle' ctx.fillText(text, x, y) }) }) } } const Component = () => { ChartJS.register(ArcElement, Tooltip, Legend, alwaysTooltipPlugin) ChartJS.overrides.pie.plugins.legend.position = 'right' const data = { labels: ['奥州ロマン', 'シナノゴールド', 'ピンクレディ', 'ブラムリー'], datasets: [ { label: '購入数', data: [1, 5, 3, 2], backgroundColor: [ 'firebrick', 'gold', 'pink', 'mediumseagreen' ], borderColor: [ 'firebrick', 'gold', 'pink', 'mediumseagreen' ], borderWidth: 1 } ] } return ( <Pie data={data} /> ) } export const Route = createLazyRoute('/pie_chart/pie_chart_with_always_text')({ component: Component })
実装が終わったので、ブラウザで http://localhost:5173/pie_chart/pie_chart_with_always_text
を開くと、各領域の上にラベルが表示されました。
なお、各領域のラベルの位置は調整していないため、やや重なっています。ただ、今回はサンプルコードなので気にしないこととします。
ちなみに、マウスオーバーすると、今まで通り Tooltip が表示されます。
Pie Chartの上にTooltipを常に表示する
ラベルが常に表示できるとすれば、Tooltipも常に表示できるかもしれないと考えました。
そこで、
- 各領域の上に Tooltip を常に表示する
- マウスオーバーしても、デフォルトの Tooltip は表示しない
の方法を調べたところ、Chart.js の公式 Youtube に情報がありました。
How to Always Show Tooltip on Pie Chart in Chart js - YouTube
そこで、Youtubeの内容を参考にしながら pie_chart_with_always_tooltip.lazy.tsx
を実装してみました。
実装したときのメモは以下です。
ctx.beginPath()
以降の実装で、Tooltipを描画する- Pieコンポーネントの
options
に対して、マウスオーバーしたときのTooltipを表示しないように設定する
import {ArcElement, Chart as ChartJS, Legend, Tooltip} from "chart.js"; import {Pie} from "react-chartjs-2"; import {createLazyRoute} from "@tanstack/react-router"; const alwaysTooltipPlugin = { id: 'alwaysShowTooltip', afterDraw(chart: ChartJS) { const {ctx} = chart ctx.save() chart.data.datasets.forEach((_dataset, i) => { chart.getDatasetMeta(i).data.forEach((datapoint, index) => { const {x, y} = datapoint.tooltipPosition(false) const text = chart.data.labels ? (chart.data.labels[index] ?? '' + ': ' + chart.data.datasets[i].data[index]).toString() : '' const textWidth = ctx.measureText(text).width ctx.fillStyle = 'rgba(0, 0, 0, 0.8)' ctx.fillRect(x - (textWidth + 10) / 2, y - 25, textWidth + 10, 20 ) // triangle ctx.beginPath() ctx.moveTo(x, y) ctx.lineTo(x - 5, y - 5) ctx.lineTo(x + 5, y - 5) ctx.fill() ctx.restore() // text ctx.font = '12px Arial' ctx.fillStyle = 'white' ctx.fillText(text, x - (textWidth / 2), y - 10) ctx.restore() }) }) } } const options = { plugins: { tooltip: { enabled: false } } } const Component = () => { ChartJS.register(ArcElement, Tooltip, Legend, alwaysTooltipPlugin) ChartJS.overrides.pie.plugins.legend.position = 'right' const data = { labels: ['奥州ロマン', 'シナノゴールド', 'ピンクレディ', 'ブラムリー'], datasets: [ { label: '購入数', data: [1, 5, 3, 2], backgroundColor: [ 'firebrick', 'gold', 'pink', 'mediumseagreen' ], borderColor: [ 'firebrick', 'gold', 'pink', 'mediumseagreen' ], borderWidth: 1 } ] } return ( <Pie data={data} options={options} /> ) } export const Route = createLazyRoute('/pie_chart/pie_chart_with_always_tooltip')({ component: Component })
実装が終わったので、ブラウザで http://localhost:5173/pie_chart/pie_chart_with_always_tooltip
を開くと、各領域の上にラベルが表示されました。
なお、各領域のTooltipの位置は調整していないため、やや重なっています。ただ、今回はサンプルコードなので気にしないこととします。
ソースコード
Githubに上げました。
https://github.com/thinkAmi-sandbox/react_chartjs-example
今回のプルリクはこちら。
https://github.com/thinkAmi-sandbox/react_chartjs-example/pull/1