こんにちは。エンジニアリング事業本部の@gc_tech70です。 普段はNuxt.js、TypeScriptを使ってフロントエンド領域をメインに開発をしています。
今回は弊社サービスであるスマートマットライトにて、消費レポート(グラフ)機能を実装したため、Nuxt.js、TypeScript環境でApexCharts.jsを利用してグラフ機能を実装したときの技術選定、具体的な実装方法やハマりどころなどについて紹介したいと思います。
https://service.lite.smartmat.io/news/consumption-report_release_20211015
今回グラフ機能を実装するためにApexCharts.jsを採用しましたが、選定した理由については以下のようになっています。
グラフ機能を実装する際の要件はざっくり以下の通りです。
BtoCサービスなので、できるだけリッチに見せたい
レスポンシブに対応したい
Vueで使えるグラフライブラリ自体は結構ありますが、ざっくり調べた感じドキュメント量やスター数などを見るとvue-chart.jsかApexCharts.jsの二択かなという印象だったので、今回はその2つで比較をしていきます。
参考記事: https://qiita.com/engineerYodaka/items/7d86de58959ca633716d
Chart.jsをVueで使うためのラッパーライブラリ
上記比較をした結果、カスタムできる要素の多さ、グラフの種類、メンテナンス頻度などにおいては特に差はない印象でした。
「レスポンシブに対応している。(グラフのデータが多い場合、できれば横スクロールしたい)」という要件に関しても、どちらもレスポンシブには対応しているものの、描画時点の画面サイズに合わせたグラフ画像を生成するような動きとなっているようで、横スクロールには対応してなく、差はないようです。
「グラフ内に基準線を表示したい」という要件に関しては、ApexCharts.jsの方がグラフ内の任意のポイントに点や線を配置することができ、vue-chart.jsでも基準線を表示することはできそうなのですが、標準機能にはなく追加で別のプラグインを入れる必要があり、chart.jsのバージョンによっては動かないこともあるそうなので、この点においてはApexCharts.jsの方が軍配が上がりそうです。
また、ApexCharts.jsには標準でCSVやグラフ画像のダウンロード機能がついていたり、グラフ内に画像を挿入できたりと、追加の要件があった際の拡張性の面でも期待できそうです。
ドキュメントについてはどちらも豊富ですが、強いて言うならvue-chart.jsではグラフの設定はChart.jsの公式を調べる必要がある一方で、ApexCharts.jsは公式で細かい設定のVueのサンプルコードが公開されており、個人的にはApexCharts.js調べやすい印象でした。
UI的な部分もvue-chart.jsはシンプルでApexCharts.jsの方がリッチな印象です。(スタイルやアニメーションの設定をしっかりすればどちらも近いところまで寄せることはできると思いますが)
上記のような点から今回はApexCharts.jsを採用することに決めました。
記法はOptions API(Vue.extend)を採用しています。
参考リンク: https://github.com/apexcharts/vue-apexcharts
yarn add apexcharts vue-apexcharts
or
npm install --save apexcharts vue-apexcharts
/plugings/vue-apexcharts.ts
import VueApexCharts from 'vue-
apexcharts'
Vue.use(VueApexCharts)
Vue.component('apexchart', VueApexCharts)
nuxt.config.ts
plugins: [
'@/plugins/vue-apexcharts',
],
ApexChartsSample.vue
<template>
<div>
<apexchart
width="500"
type="line"
:options="chartOptions"
:series="series"
></apexchart>
</div>
</template>
<script lang="ts">
import Vue from 'vue'
import { ApexOptions } from 'apexcharts'
export default Vue.extend({
data(): {
chartOptions: ApexOptions
series: ApexAxisChartSeries
} {
return {
chartOptions: {
chart: {
id: 'vuechart-example',
},
xaxis: {
categories: [1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998],
},
},
series: [
{
name: 'グラフ名',
data: [30, 40, 35, 50, 49, 60, 70, 91],
},
],
}
},
})
</script>
ApexCharts.jsではxaxisのtypeに datetime
を指定することでデータが日付形式の場合、きれいにレイアウトに収まるように自動で一部の日付を省略してくれます。
export default Vue.extend({
data(): {
chartOptions: ApexOptions
series: ApexAxisChartSeries
} {
return {
chartOptions: {
chart: {
id: 'vuechart-example',
},
xaxis: {
type: 'datetime',
categories: [
'2021-09-26',
'2021-09-27',
'2021-09-28',
'2021-09-29',
'2021-09-30',
'2021-10-03',
'2021-10-04',
'2021-10-05',
],
labels: {
format: 'MM/dd', // 日付のフォーマット
},
},
},
series: [
{
name: 'グラフ名',
data: [157, 135, 135, 113, 99, 99, 46, 46],
},
],
}
},
})
しかし、一方でデータを全て見せたい時にはこの機能を使うと省略されてしまうので、そういう場合はtypeに category
を指定することで全てのデータが表示されます。
※ただし、 category
は文字列型のようなイメージなので表示したい日付のフォーマットには事前に変換しておく必要があります。
<script lang="ts">
import Vue from 'vue'
import { ApexOptions } from 'apexcharts'
export default Vue.extend({
data(): {
chartOptions: ApexOptions
series: ApexAxisChartSeries
} {
return {
chartOptions: {
chart: {
id: 'vuechart-example',
},
xaxis: {
type: 'category',
categories: [
'2021-09-26',
'2021-09-27',
'2021-09-28',
'2021-09-29',
'2021-09-30',
'2021-10-03',
'2021-10-04',
'2021-10-05',
],
},
},
series: [
{
name: 'グラフ名',
data: [157, 135, 135, 113, 99, 99, 46, 46],
},
],
}
},
})
</script>
ApexCharts.jsではデータの値によってY軸の最大、最小値を自動で近い数値に設定してくれます。 基本的には表示する値に近い数値がY軸の目盛りに設定され、見やすく切りのいい数値が設定されます。 また、最大、最小値に関しては自身で設定することもでき、例えば最小値0を設定するとマイナスの値があった時にグラフ中に表示されなくなります。 しかし、最大、最小値を設定した場合、グラフ中の最小値から最大値が10分割された値が目盛りに設定され、値によってはとても見づらくなります。 これの対策としては、最大、最小値を安易に設定しないことや、最大、最小値には切りのいい数値を設定することで解消できます。
<script lang="ts">
import Vue from 'vue'
import { ApexOptions } from 'apexcharts'
export default Vue.extend({
data(): {
chartOptions: ApexOptions
series: ApexAxisChartSeries
} {
return {
chartOptions: {
chart: {
id: 'vuechart-example',
},
xaxis: {
type: 'datetime',
categories: [
'2021-09-26',
'2021-09-27',
'2021-09-28',
'2021-09-29',
'2021-09-30',
'2021-10-03',
'2021-10-04',
'2021-10-05',
],
labels: {
format: 'MM/dd',
},
},
},
series: [
{
name: 'グラフ名',
data: [157, 135, 135, 113, 99, 99, 46, 46],
},
],
}
},
})
</script>
<script lang="ts">
import Vue from 'vue'
import { ApexOptions } from 'apexcharts'
export default Vue.extend({
data(): {
chartOptions: ApexOptions
series: ApexAxisChartSeries
} {
return {
chartOptions: {
chart: {
id: 'vuechart-example',
},
xaxis: {
type: 'datetime',
categories: [
'2021-09-26',
'2021-09-27',
'2021-09-28',
'2021-09-29',
'2021-09-30',
'2021-10-03',
'2021-10-04',
'2021-10-05',
],
labels: {
format: 'MM/dd',
},
},
yaxis: {
min: 0, // 最小値
},
},
series: [
{
name: 'グラフ名',
data: [157, 135, 135, 113, 99, 99, 46, 46],
},
],
}
},
})
</script>
グラフのtypeをdatetimeで指定してJSTのデータを渡した際、デフォルトの設定だとUTCで解釈されてしまい、例えば'2021-10-04T03:50:03+09:00'という日付は、日本時間で'2021/10/04 03:50:03'の日時であることを意味していますが、グラフ上では9時間前の'2021/10/03 18:50:03'として解釈されていまいます。 これの対策としては、xaxis.labels.datetimeUTCにfalseを設定することでJSTのようなUTC以外のタイムゾーンの日時でも正常に解釈されるようです。
<script lang="ts">
import Vue from 'vue'
import { ApexOptions } from 'apexcharts'
export default Vue.extend({
data(): {
chartOptions: ApexOptions
series: ApexAxisChartSeries
} {
return {
chartOptions: {
chart: {
id: 'vuechart-example',
},
xaxis: {
type: 'datetime',
categories: [
'2021-10-04T03:50:03+09:00',
'2021-10-05T03:50:03+09:00',
],
labels: {
format: 'MM/dd',
showDuplicates: false,
hideOverlappingLabels: true,
},
},
tooltip: {
x: {
format: 'yyyy/MM/dd',
},
},
},
series: [
{
name: 'グラフ名',
data: [70, 91],
},
],
}
},
})
</script>
<script lang="ts">
import Vue from 'vue'
import { ApexOptions } from 'apexcharts'
export default Vue.extend({
data(): {
chartOptions: ApexOptions
series: ApexAxisChartSeries
} {
return {
chartOptions: {
chart: {
id: 'vuechart-example',
},
xaxis: {
type: 'datetime',
categories: [
'2021-10-04T03:50:03+09:00',
'2021-10-05T03:50:03+09:00',
],
labels: {
format: 'MM/dd',
showDuplicates: false,
hideOverlappingLabels: true,
datetimeUTC: false, // ここに追加
},
},
tooltip: {
x: {
format: 'yyyy/MM/dd',
},
},
},
series: [
{
name: 'グラフ名',
data: [70, 91],
},
],
}
},
})
</script>
グラフのデータを更新したい時はオブジェクトの単一プロパティを更新しても、変更を検知できずグラフは更新されないのでオブジェクトそのものを上書きする必要があります。 これについては公式にも書いてあります。
https://github.com/apexcharts/vue-apexcharts
誤った例
this.chartOptions.xaxis = {
labels: {
style: {
colors: ['red']
}
}
}}
正しい例
this.chartOptions = {...this.chartOptions, ...{
xaxis: {
labels: {
style: {
colors: ['red']
}
}
}
}}
ApexCharts.jsを使ってみた感想は、導入が簡単、拡張性が高い、デフォルトでUIが整っているという印象でした。 結構ハマりどころもありましたが、大体どのグラフライブラリも似たような感じなんじゃないかなと思いました。 個人的には結構おすすめのライブラリなので、グラフの実装を検討している方は是非使ってみてください!