Chart.jsで作った円形グラフの値をスライダーで操作する

この記事は[ウェブクルー Advent Calendar 2023](https://qiita.com/advent-calendar/2023/webcrew) 3日目の記事です。

## 概要
はじめての個人開発でChart.jsを使用し、スライダーでグラフを操作出来るようにしたのが楽しかったという記録です。
ChatGPTにChart.jsのコードの生成をお願いしたら古いバージョンのコードがお出しされて困ったので、円形グラフの初期設定などにも役立つかもしれません。

## 完成図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953428/4812d155-2ec9-8dbe-a6c6-5d2755a3a4eb.png)
下のスライダーを変化させることで、上のグラフが更新される仕様です。

## コード
以下の2ファイルを同階層に置きます。

### HTML
```index.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>

<!--本体部分-->
<div>
    <div>
        <canvas id="circleChart"></canvas>
    </div>
    <input type="range" id="pointInput" name="point" min="0" max="100">
</div>
<!-- 本体ここまで-->

<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.0.0"></script>
<script src="./index.js"></script>

</html>
```

### JavaScript
```index.js
// 定数：グラフの色設定
const CHART_COLORS = {
    blue: 'rgb(63, 81, 181)',
    yellow: 'rgb(255, 235, 59)',
    // orange: 'rgb(255, 159, 64)',
    // green: 'rgb(75, 192, 192)',
    // deepblue: 'rgb(0, 0, 230)',
    // red: 'rgb(255, 99, 132)',
    // purple: 'rgb(153, 102, 255)',
    // grey: 'rgb(201, 203, 207)'
};

// グラフの初期設定
function createInitialChartData() {
    const pointInputValue = document.getElementById("pointInput").value;
    return {
        labels: ['悪い点', '良い点'],
        datasets: [{
            data: [pointInputValue, 100 - pointInputValue],
            backgroundColor: Object.values(CHART_COLORS),
        }]
    };
}

// グラフの設定オプション
const chartOptions = {
    responsive: true,
    plugins: {
        legend: { display: false },
        datalabels: {
            // スライダーの値を取得して、ラベルとして表示している。
            formatter: (value, context) => `${context.chart.data.labels[context.dataIndex]}\n${value}%`,
            color: ["#fff", "#000"],
            font: { weight: "bold", size: 20 }
        }
    }
};

// グラフの初期化
function initializeChart() {
    const ctx = document.getElementById('circleChart').getContext('2d');
    return new Chart(ctx, {
        type: 'pie',
        data: createInitialChartData(),
        plugins: [ChartDataLabels],
        options: chartOptions
    });
}

// グラフのサイズ設定　グラフのサイズ設定がCSSで行えないため、JavaScriptで制御する。
function setChartSize(chart) {
    chart.canvas.parentNode.style.height = '240px';
    chart.canvas.parentNode.style.width = '240px';
}

// グラフの更新
function updateChart(chart, good, bad) {
    chart.data.datasets.forEach(dataset => {
        dataset.data = [bad, good];
    });
    chart.update('none');
}

// スライダーのイベントリスナー設定
function setupSliderListener(chart) {
    document.getElementById("pointInput").addEventListener("input", function () {
        const goodPoint = this.value;
        const badPoint = 100 - goodPoint;
        updateChart(chart, goodPoint, badPoint);
    });
}

// メイン処理
const pieChart = initializeChart();
setChartSize(pieChart);
setupSliderListener(pieChart);
```

## 解説
`createInitialChartData`で、グラフの初期データを設定しています。
labelとdataの配列にそれぞれデータをカンマ区切りで追加することで、グラフの値を増やすことが出来ます。その際は`updateChart`の引数と`setupSliderListener`の呼び出し部にも新しいデータを追加しておきましょう。

グラフの色を設定する場合には`CHART_COLORS`、グラフのラベル文字色を変更する場合には`chartOptions`のdatalabelsの設定を変更すれば彩り豊かにすることが出来るでしょう。

```javascript
function createInitialChartData() {
    const pointInputValue = document.getElementById("pointInput").value;
    return {
        labels: ['悪い点', '良い点'],//←ここにラベルを追加する
        datasets: [{
            data: [pointInputValue, 100 - pointInputValue], //←ここに値を追加する
            backgroundColor: Object.values(CHART_COLORS),//←色の設定はCHART_COLORSで
        }]
    };
}
```

## 終わりに
本職はJavaエンジニアになるのですが、ここ1年で伸びたのが社内ドメイン知識となるため何も書けませんでした。
コードを読む技術は確実に上がったのですが、なかなかネタにしにくい話なのが悩みです。
来年はインプットと個人開発へさらに手を出していき、~~投稿ネタ確保を~~頑張っていきたいところです。
明日は@wc-horinoさんの投稿になります。お楽しみに！


ウェブクルーでは一緒に働いてくれる方を絶賛募集中です！
興味のある方はぜひお問い合わせください。
[会社ホームページ](https://www.webcrew.co.jp/recruit/)

## 参考
[Chart.jsの公式サイト](https://www.chartjs.org/docs/latest/)