Vue3でレーダーチャートを実装する必要が出てきたため、手元で動かしてみるために必要そうな情報をまとめます。
使用するライブラリとしては、差し当たりGitHubのスター数が多かったvue-chartjs
を対象としています。
検証環境の準備
create-vue
コマンドで初期化します。
検証用かつJavaScriptでの実装を想定しているため、プラグインの追加は最小限で良いものとします。
❯ npm init vue@latest
Vue.js - The Progressive JavaScript Framework
✔ Project name: … vue3-radar-chart
✔ Add TypeScript? … No
✔ Add JSX Support? … No
✔ Add Vue Router for Single Page Application development? … No
✔ Add Pinia for state management? … No
✔ Add Vitest for Unit Testing? … No
✔ Add Cypress for both Unit and End-to-End testing? … No
✔ Add ESLint for code quality? … Yes
✔ Add Prettier for code formatting? … Yes
vue-chartjsのリファレンスのinstallationより必要なパッケージをインストールします。
npm i vue-chartjs chart.js
この時点で追加されたパッケージのバージョンは以下の通り。
{
"name": "vue3-radar-chart",
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview --port 4173",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore"
},
"dependencies": {
"chart.js": "^3.9.1",
"vue": "^3.2.38",
"vue-chartjs": "^4.1.2"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.1.4",
"@vitejs/plugin-vue": "^3.0.3",
"@vue/eslint-config-prettier": "^7.0.0",
"eslint": "^8.22.0",
"eslint-plugin-vue": "^9.3.0",
"prettier": "^2.7.1",
"vite": "^3.0.9"
}
}
レーダーチャートの実装
vue-chartjsのリファレンスの《Examples》にある実装例を参考にします。
特に必要性はないですが、単一ファイルコンポーネントへの書き換えを行ってみます。JSファイルとして管理したほうが良いのでしょうか。
サンプルはTypeScriptで実装されていますが、今回はJavaScriptでの実装を想定しているため型情報を落とします。
<script setup>
import RadarChart from './components/RadarChart.vue'
</script>
<template>
<RadarChart
v-bind:chartId="'my-radar-chart'"
v-bind:cssClasses="'my-radar-chart-container'" />
</template>
<style>
/* assets/main.cssは全行コメントアウト */
.my-radar-chart-container {
margin: 0 auto;
max-width: 500px;
}
</style>
<script>
import { defineComponent, h } from 'vue'
import { Radar } from 'vue-chartjs'
import {
Chart as ChartJS,
Title,
Tooltip,
Legend,
PointElement,
RadialLinearScale,
LineElement
} from 'chart.js'
ChartJS.register(
Title,
Tooltip,
Legend,
PointElement,
RadialLinearScale,
LineElement
)
export default defineComponent({
name: 'RadarChart',
components: {
Radar
},
props: {
chartId: {
default: 'radar-chart'
},
width: {
default: 400
},
height: {
default: 400
},
cssClasses: {
type: String
},
styles: {
default: () => {}
},
plugins: {
default: () => []
}
},
setup(props) {
const chartData = {
labels: [
'Eating',
'Drinking',
'Sleeping',
'Designing',
'Coding',
'Cycling',
'Running'
],
datasets: [
{
label: 'My First dataset',
backgroundColor: 'rgba(179,181,198,0.2)',
borderCapStyle: 'square',
borderColor: 'rgba(179,181,198,1)',
pointBackgroundColor: 'rgba(179,181,198,1)',
pointBorderColor: '#fff',
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgba(179,181,198,1)',
data: [65, 59, 90, 81, 56, 55, 40]
},
{
label: 'My Second dataset',
backgroundColor: 'rgba(255,99,132,0.2)',
borderColor: 'rgba(255,99,132,1)',
pointBackgroundColor: 'rgba(255,99,132,1)',
pointBorderColor: '#fff',
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgba(255,99,132,1)',
data: [28, 48, 40, 19, 96, 27, 100]
}
]
}
const chartOptions = {
responsive: true,
maintainAspectRatio: false
}
return () =>
h(Radar, {
chartData,
chartOptions,
chartId: props.chartId,
width: props.width,
height: props.height,
cssClasses: props.cssClasses,
styles: props.styles,
plugins: props.plugins
})
}
})
</script>
基本的にはvue-chartjsからインポートしたRadar
コンポーネントを第1引数に、props
、chartData
、chartOptions
をオブジェクトリテラルでまとめたものを第2引数として Vueの h() 関数
に渡し、さらにそれをRender関数
に渡して描画させるという構造のようです。
return () =>
h(Radar, {
chartData,
chartOptions,
chartId: props.chartId,
width: props.width,
height: props.height,
cssClasses: props.cssClasses,
styles: props.styles,
plugins: props.plugins
})
詳細の確認は省略しますが、h()関数の第2引数にはvue-chartjsのRadarコンポーネントに定義されたプロパティを渡すという理解で良さそうです。
また、余談としてRadarChart.vueも<script setup>
の記法で書きたかったのですが、以下の通りRender関数との相性が悪いようでした。
このケースでは render 関数はサポートされていません。代わりに、通常の <script> と setup オプションを使用してください。
以下に各プロパティ設定値の定義についての参照先を記載します。
chartId / width / height / cssClasses / styles
グラフが実際に描画される際には<div>タグ、<canvas>タグの入れ子になるようです。開発者ツールで確認することができます。
そしてこれらの設定値は以下のvue-chartjsの実装から、それぞれのタグに対する属性指定であると見えます。
vue-chartjs/src/BaseCharts.ts L263-271
return () =>
h('div', { style: props.styles, class: props.cssClasses }, [
h('canvas', {
id: props.chartId,
width: props.width,
height: props.height,
ref: canvasEl
})
])
chartData
データ構造はChart.js
リファレンスのRadar Chart
の《Dataset Properties》の記載に準拠するようです。
Namespace: dataに対応。
以下のリファレンスを確認します。
chartOptions
データ構造はChart.js
リファレンスの《Configuration》配下の記載に準拠するようです。
Namespace: optionsに対応。
差し当たり、responsive
、maintainAspectRatio
については以下のリファレンスを確認します。
Appendix
このままの実装だとChart.jsのRadar Chartのサンプルの見た目と異なり、線で囲われたエリアの背景色が付かない点が気になります。
関連情報を調べたところ、Chart.jsからFiller
モジュールをインポートすれば背景色が付くことがわかりました。
リファレンスとしてはこちらに記載があるようです。
import {
Chart as ChartJS,
Title,
Tooltip,
Legend,
PointElement,
RadialLinearScale,
LineElement,
Filler // 追加
} from 'chart.js'
ChartJS.register(
Title,
Tooltip,
Legend,
PointElement,
RadialLinearScale,
LineElement,
Filler // 追加
)
インポートして登録するだけでで有効になるようですが、fill
プロパティで明示化したほうが良いかもしれません。