前回の記事で 作成した Vue アプリケーションのダッシュボード画面に、グラフを描画してみました。
ライブラリは Vue でグラフ描画するためのライブラリ
vue-chart.js
を使いました。また、データについてはまだサーバーとの連携をしていないので、サンプルとしてランダム関数を使って適当に作成しています。
*Vue-chart.js とは
Chart.js を Vue で使うためのラッパーです。グラフコンポーネントを簡単に作成することができます。
*Chart.js とは
シンプルで柔軟性に富んだグラフを作成することができる、グラフ描画ライブラリです。HTML5の
canvas
要素で様々な種類のチャートを作成することができます。*環境
- MacOS
- chart.js 2.8.0
- vue-chartjs 3.4.2
*参照
*インストール
Vueプロジェクトは下記記事で作成したものを使いました。プロジェクトディレクトリの直下で下記コマンドを実行し、インストールします。
$ npm install vue-chartjs chart.js --save
*コンポーネント設計
下記コンポーネント設計にしました。ダッシュボード画面からグラフカードのコンポーネントを呼び出し、そのグラフカードで折れ線グラフのコンポーネントを呼び出します。
DashBoard.vue <-- GraphCard.vue <-- LineChart.vue
DashBoard
---- グラフに使うデータを取得GraphCard
---- グラフのサイズや色を指定LineChart
----DashBoard
とGraphCard
から受け取ったデータを使ってグラフを描画
*Vue-chartjs の使い方
まず、折れ線グラフのコンポーネントをLineChart.vue
として新規作成します。vue-chartjs
から作成したいグラフの種類をimport
し、extends
に追加します。次に
mounted()
のなかでrenderChart()
を呼び出し、引数にdata, options
のプロパティを渡すとグラフを描画することができます。渡すデータ形式については こちらのサイト を参考にしました。
今回は呼び出し元から
props
で値を受け取るようにしたので、このコンポーネントではデータの作成はしていません。引数で渡すdata, options
の値が変更されると再レンダリングされます。<src/components/atoms/LineChart.vue>
<script>
import { Line } from "vue-chartjs";
export default {
extends: Line,
props: {
data: Object,
options: Object
},
mounted() {
this.renderChart(this.data, this.options);
}
};
</script>
補足ですが、
data
とoption
は下記形式でオブジェクトとして定義します。(必要な設定だけ追加します)今回この設定は
GraphCard.vue
で行なっています。data: {
labels: ['2019/7/1','2019/7/2','2019/7/3','2019/7/4','2019/7/5'], // X軸のラベル名
datasets: [{
data: [100, 340, 554, 10, 635], //グラフで使うデータ
backgroundColor: '#3280f57e //背景色
}]
},
options: {
responsive: true, //グラフサイズの自動調整
legend: {
display: false //凡例の非表示
},
title: {
display: true, //タイトルの表示
fontSize: 18, //フォントサイズ
text: 'タイトル' //グラフ名表示
},
scales: {
yAxes: [{
display: true, //Y軸の表示
scaleLabel: {
display: true, //Y軸のラベル表示
labelString: 'Y', //Y軸のラベル
fontSize: 18 //Y軸のラベルのフォントサイズ
},
ticks: {
min: 0, //Y軸の最小値
max: 30, //Y軸の最大値
fontSize: 18, //Y軸のフォントサイズ
stepSize: 5 //Y軸の間隔
},
}],
xAxes: [{
display: true, //X軸の表示
scaleLabel: {
display: true, //X軸の表示
labelString: 'X', //X軸のラベル
fontSize: 18 //X軸のラベルのフォントサイズ
},
ticks: {
fontSize: 18 //X軸のフォントサイズ
},
}],
},
layout: {
padding: {
left: 100,
right: 50,
top: 0,
bottom: 0
}
}
}
*子コンポーネントの呼び出し
GraphCard.vue
から上で作成したLineChart.vue
を呼び出します。LineChart.vue
をインポートし、<template>
で表示させたい箇所に<line-chart>
を追加します。このとき、height
とwidth
のプロパティを設定することで、表示するグラフのサイズを変更することができます。このコンポーネントでグラフの色やオプションの定義をしています。
今回は一時的なサンプルデータを使ったので、グラフの Y軸の最小値、最大値、間隔を指定しましたが、ここは使うデータによって任意の値に修正します。(オプションを何も設定しなくてもグラフの描画はできます)
<src/components/molecules/GraphCard.vue>
<template>
<div class="graph">
<card-title :value="name"></card-title>
<!---- ↓追加 ---->
<line-chart class="chart" :data="getChartData()" :options="getOptions()" :height="270" :width="770"></line-chart>
</div>
</template>
<script>
import CardTitle from "../atoms/CardTitle";
import LineChart from "../atoms/LineChart"; //←追加
export default {
components: {
CardTitle,
LineChart //←追加
},
props: {
name: String,
data: Array, //←追加
label: Array //←追加
},
<!---- ↓追加 start ---->
data() {
return {
graphData: null
};
},
methods: {
getChartData() {
return {
labels: this.label,
datasets: [
{
data: this.data,
borderColor: "#3280f57e",
backgroundColor: "#3280f528",
animation: false
}
]
};
},
getOptions() {
// TODO: fix min and max value
return {
legend: {
display: false
},
scales: {
yAxes: [
{
ticks: {
min: 200,
max: 600,
stepSize: 100
}
}
]
}
};
}
<!---- ↑追加 end ---->
}
};
</script>
<style scoped lang="scss">
.graph {
display: inline-block;
margin: 0 0 40px 40px;
height: 300px;
width: 770px;
background-color: white;
border: 0 solid #aaa;
border-radius: 4px;
box-shadow: 2px 2px 2px 2px #ccc;
padding: 15px;
}
// ↓追加
.chart {
padding-top: 15px;
}
</style>
さらにダッシュボード画面からカードのコンポーネントを呼び出します。
Dashboard.vue
にGraphCard.vue
をインポートする処理を追加します。Dashboard.vue
ではグラフに使うデータを作成して子コンポーネントに渡しています。Math.floor(Math.random() * Math.floor())
でランダムな整数を作成します。さらに整数の範囲を 300 〜 500 の値にしたかったため、300 + Math.floor(Math.random() * Math.floor(500 - 300))
としています。このランダムな整数を 30日分(1ヵ月)作成したかったので、30回分ループさせるために
[...Array(30)]
とmap()
を使いました。const result = [...Array(30)].map(
() => 300 + Math.floor(Math.random() * Math.floor(500 - 300)))
また、X軸のラベルに今月の日時を表示させたかったので、今月の初日を取得し、1日ずつ加算した日付の文字列を配列にして作成しています。
const date = new Date();
date.setDate(0);
const result = [...Array(30)].map(() => {
date.setDate(date.getDate() + 1);
return date.toLocaleDateString();
});
Dashboard.vue
の全体は下記になりました。<src/components/pages/Dashboard.vue>
<template>
<div>
<navbar></navbar>
<sidebar></sidebar>
<div class="top">
<div>
<value-card name="ユーザー数 合計" value="256"></value-card>
<value-card name="プレビュー数 合計" value="21,234"></value-card>
</div>
<!---- ↓ data,labelを追加 ---->
<graph-card name="ユーザー数 推移" :data="getUserData" :label="getLabelList"></graph-card>
<graph-card name="プレビュー数 推移" :data="getPreviewData" :label="getLabelList"></graph-card>
<list-card name="データ一覧"></list-card>
</div>
</div>
</template>
<script>
import Navbar from "../organisms/Navbar";
import Sidebar from "../organisms/Sidebar";
import ValueCard from "../molecules/ValueCard";
import GraphCard from "../molecules/GraphCard";
import ListCard from "../molecules/ListCard";
export default {
components: {
Navbar,
Sidebar,
ValueCard,
GraphCard,
ListCard
},
<!---- ↓追加 start ---->
computed: {
getUserData() {
// TODO: fix getting from server data
return [...Array(30)].map(
() => 300 + Math.floor(Math.random() * Math.floor(500 - 300))
);
},
getLabelList() {
const date = new Date();
date.setDate(0);
return [...Array(30)].map(() => {
date.setDate(date.getDate() + 1);
return date.toLocaleDateString();
});
},
getPreviewData() {
// TODO: fix getting from server data
return [...Array(30)].map(
() => 300 + Math.floor(Math.random() * Math.floor(500 - 300))
);
}
}
<!---- ↑追加 end ---->
};
</script>
<style scoped lang="scss">
.top {
padding: 66px 0 0 200px;
}
</style>
*所感
グラフ描画をコンポーネント化できるので、1度作成してしまえば設定を変更したグラフもすぐ作成できて便利でした。ただ、データを作成する箇所を間違えると汎用的に使えなくなってしまうので、設計はよく考慮する必要がありそうです。Sign up here with your email
ConversionConversion EmoticonEmoticon