chartjsでドーナツチャートを作った。
tooltipの表示調整やpluginsを使った追加情報表示を実装したので、残しておく。
確認環境
- macOS 13.2
- docker desktop 4.16.2
- vue 3.2.45
- chartjs 2.9.4
- vue-chartjs 3.5.1
ドーナツチャートの作り方
canvasタグにドーナツチャートを描画する
data, options, pluginsをそれぞれ宣言して、new Chartに()に渡す
template
<div style="position: relative; width: 80vw; height: 50vh">
<canvas id="canvas" />
</div>
script
import { onMounted } from "vue";
import { Chart } from "chart.js";
...
onMounted(() => {
const ctx = document.getElementById("canvas");
if (ctx)
new Chart(ctx, {
type: "doughnut",
data,
options,
plugins,
});
});
data
色・値・ラベルをそれぞれ指定する
const data = {
datasets: [
{
backgroundColor: ["#d3f8e2", "#e4c1f9", "#f694c1", "#ede7b1"],
data: [46, 43, 7, 4],
},
],
labels: ["January", "February", "March", "April"],
};
options
const options = {
legend: { display: true },
maintainAspectRatio: false, // サイズ変更時に元のキャンバスのアスペクト比を維持
cutoutPercentage: 50, // ドーナツの太さ(細 > 太)
tooltips: {
enabled: true,
intersect: false, // マウスの位置が要素と交差する場合にのみ表示
filter: (context, TooltipItem) => // 値が 0 より大きいものだけを表示
TooltipItem.datasets[0].data[context.index] > 0,
callbacks: {
// January: 84% のように表示
label: (context, TooltipItem) => {
return (
TooltipItem.labels[context.index] +
":" +
TooltipItem.datasets[0].data[context.index] +
"%"
);
},
},
},
layout: {
// グラフ外枠のパッディング指定
padding: {
left: 20,
right: 20,
},
},
};
maintainAspectRatioについて
maintainAspectRatio をtrue(デフォルト値)にしておくと、グラフが小さく描画されてしまう。
そのため、「maintainAspectRatio=false」にして、
canvasの親要素(div)に「position: relative; width: 80vw; height: 50vh」をつけて大きさを調整した。
cutoutPercentageについて
ドーナツの太さが変更できる
50はデフォルト値、0にすると円グラフになる、100にすると描画されない
tooltipsについて
intersectをfalseにしておく方が操作性が高く感じる
filterやcallbacks.labelで表示制御や表示文字列調整が可能
layoutについて
paddingだけ指定ができて、チャートを横並びにした時などで便利
px指定
plugins
それぞれの値の常時表示と中央に合計値を描画する
afterDatasetsDrawやafterDrawが、チャートを描画したときやマウスオーバー時に発火される
何度も呼ばれるので、シンプルな処理にすべき
chart.data.datasetsをループして、fillTextで文字を表示させている
中央に表示する位置は、数値でしか調整ができなくて、描画文字数分をpadding変数で調整している
const plugins = [
{
afterDatasetsDraw(chart) {
const ctx = chart.ctx;
chart.data.datasets.forEach((dataset, i) => {
let meta = chart.getDatasetMeta(i);
if (!meta.hidden) {
meta.data.forEach(function (element, index) {
let fontSize = 12;
let fontStyle = "normal";
let fontFamily = "Helvetica Neue";
ctx.fillStyle = "#000";
ctx.font = Chart.helpers.fontString(
fontSize,
fontStyle,
fontFamily
);
ctx.textAlign = "center";
ctx.textBaseline = "middle";
let dataString = dataset.data[index] + "%";
let padding = 0;
let position = element.tooltipPosition();
// グラフ値を常に表示
ctx.fillText(
dataString,
position.x,
position.y + fontSize / 2 - padding
);
});
}
});
let title = "合計100%";
let fontSize = 21;
let padding = -10;
ctx.fillStyle = "#000";
ctx.font = Chart.helpers.fontString(fontSize, "normal", "sans-serif");
ctx.textAlign = "center";
ctx.textBaseline = "middle";
// グラフの中心に合計を表示
ctx.fillText(
title,
chart.chart.width / 2,
chart.chart.height / 2 - padding
);
},
},
];
注意点
わざわざpluginsで表示しなくても、cssで出せば良いと最初は思ったのだが、
chartJSで表示するツールチップが重なった時に、cssで表示した文字の方がz-indexが高くなってしまい、読みにくくなってしまった。
そのため、cssでの表示から変更した。