ChartJSで円グラフのデータをリロードする

Web開発

お疲れ様です。

ChartJSはWebでグラフを描画する際のデファクトスタンダードですね。これがなかった頃はDivタグとCSSで棒グラフを描画したり、Canvasに自前で描画したりしていました。これはこれで自由度があってよかったですが、アニメーションでヌルヌル動くChartJSを使いだしてからは、もうChartJSが手放せません。

今回はそんなChartJSを使って、リロードボタンをクリックするごとにランダムな値を生成して、円グラフを表示するプログラムを実装してみたいと思います。

要するに以下のような動作をする円グラフですね。試しに何度かリロードボタンをクリックしてみてください。

クリックするたびに円グラフの中身が書き変わると思います。(なんとなくクリックしているだけでも楽しいです。)

実装したコード

まず最初に今回実装したコードを提示します。

index.htmlとindex.jsの2つのファイルを作って、それぞれに以下のコードをコピペしていただき、同じフォルダに入れて、index.htmlをブラウザで表示すると動作します。

index.html

<html>
<head>
    <script src="https://cdn.jsdelivr.net/npm/chart.js@4.3.3/dist/chart.umd.min.js"></script>
    <script src="./index.js"></script>
</head>

<body>
    <div style="max-width:400px;max-height:400px">
        <div>
            <canvas id="myChart"></canvas>
        </div>
        <button type="button" id="btn_reload">reload</button>
    </div>
</body>
</html>

index.js

"use strict";

window.addEventListener("load", () =>
{
    document.getElementById("btn_reload").addEventListener("click", btnReload_onclick);
    showChart(generateRandomData());
});

function generateRandomData()
{
    //果物の名前一覧
    let fruit_names = [
        "acerola",
        "akebia",
        "apple",
        "apricot",
        "avocado",
        "banana",
        "blackberry",
        "cherimoy",
        "chirimoy",
        "chirimuya",
        "cherry",
        "chestnut",
        "coconut",
        "cranberry",
        "custard-apple",
        "date",
        "dragon frui",
        "pitaya",
        "durian",
        "fig",
        "granadill",
        "grenadilla",
        "grapefruit",
        "grapes",
        "guarana",
        "guava",
        "Japanese aprico",
        "ume",
        "jujube",
        "kiwano",
        "kiwi",
        "lemon",
        "lime",
        "litch",
        "lyche",
        "lichee",
        "longan",
        "loquat",
        "mandarin",
        "mango",
        "mangosteen",
        "melon",
        "nectarine",
        "orange",
        "papaya",
        "passionfrui",
        "passion fruit",
        "pawpaw",
        "peach",
        "pear",
        "persimmon",
        "physalis",
        "pineapple",
        "plu",
        "Japanese plum",
        "pomegranate",
        "prun",
        "European plum",
        "quince",
        "rambutan",
        "sapodilla",
        "star frui",
        "carambola",
        "strawberry",
        "tangerine",
        "watermelon"];

    const choice_count = getRandomInt(4, Math.min(10, fruit_names.length));
    
    let labels = [];
    let count = [];
    let colors = [];
    for(let i = 0; i < choice_count; i++)
    {
        let choice_index = getRandomInt(0, fruit_names.length - 1);
        let choice_item = fruit_names.splice(choice_index, 1)[0];
        console.log("choice item", choice_item);
        labels.push(choice_item);
        count.push(getRandomInt(0, 100));
        colors.push(computeColor(i, choice_count));
    }

    return {
        labels : labels,
        datasets : [
            {
                data : count,
                backgroundColor : colors
            }
        ]
    }
}

function showChart(data)
{
    const canvas_element_id = 'myChart';
    const ctx = document.getElementById(canvas_element_id);
    let myChart = Chart.getChart(canvas_element_id);
    
    if(!myChart)
    {
        myChart = new Chart(ctx, {
            type: 'pie'
        });
    }
    myChart.data = data;
    myChart.update();
}

function getRandomInt(min, max)
{
    const l_min = Math.min(min, max)
    const l_max = Math.max(min, max)
    
    return l_min + Math.floor(Math.random() * (l_max - l_min + 1));
}

function computeColor(index, total_count)
{
    const deg = 360 / total_count * index;
    
    return "hsl(" + deg +", 100%, 50%)";
}

function btnReload_onclick()
{
    showChart(generateRandomData());
}

簡単な解説

index.htmlはほぼ説明する必要はないと思いますので、index.jsについて簡単に解説します。

処理のエントリーポイント

起動時にエントリーポイントとなるのは以下の部分です。ここでは、Windowのloadイベントに対して処理を挿入しています。Windowのloadイベントは、HTMLを構成するためのリソースの読み込みが全て完了したら発火(呼び出され)ます。

処理の内部では、リロードボタンへのクリックイベントの応答メソッドの登録と、グラフ表示処理であるshowChartメソッドの呼び出しをそれぞれ行なっています。

window.addEventListener("load", () =>
{
    document.getElementById("btn_reload").addEventListener("click", btnReload_onclick);
    showChart(generateRandomData());
});

リセットボタンの処理

Windowのloadイベントのリセットボタンをクリックすると次のメソッドが呼び出されます。(メソッド名からVB畑と言うのがバレバレですね。)

ここでも単純にチャートを表示する処理を呼び出していますね。

function btnReload_onclick()
{
    showChart(generateRandomData());
}

ランダムなデータを生成している処理

showChartメソッドの引数に対して、generateRandomDataメソッドの処理の結果を渡しています。このgenerateRandomDataメソッドでは、あらかじめ用意されたラベル名のセット(今回は果物の名称)から重複しないように数個のラベルを取り、値として0〜100の値をランダムに生成するような処理になっています。

この際、同時に色も決定します。色の計算と生成を行なっているのは、computeColorメソッドです。このcomputeColorメソッドの中で色を作成するのですが、完全なランダムでRGBを指定して色を決めてしまうと、くすんだ色が出やすくなっていましますのでHSLという色相、輝度、彩度を指定して色を決定する手法を採用しています。これにより、くすんだり、白飛びした色が出にくくなるほか、色相を一定間隔で開けることで、重複した色が発生しないようにしています。

乱数を生成するのはgetRandomIntメソッドで行なっています。これは、MDNのMath.randomを説明しているのページにある、サンプルに少し手を加えたものです。誤って最小値と最大値を逆に指定しても動作するように改造しています。

グラフを表示する部分

最終的にグラフを表示するのはshowChartメソッドです。引数に指定されたデータを元にグラフを表示します。引数にはすでにChartJS用に成形されたデータが来ることが前提となっているので、値をセットしてChartJSのupdate処理を呼び出すだけです。

ChartJSの初期化は最初に呼び出された1回のみしか実行しません。

下記コード内のコメントにも書きましたが「type : 'pie'」となっている部分を「type:'bar'」に書き換えるだけで、円グラフから棒グラフに切り替えることができます。

function showChart(data)
{
    const canvas_element_id = 'myChart';
    const ctx = document.getElementById(canvas_element_id);
    let myChart = Chart.getChart(canvas_element_id);
    
    if(!myChart)
    {
        myChart = new Chart(ctx, {
            type: 'pie'//ここをtype:'bar'にすると棒グラフになります。
        });
    }
    myChart.data = data;
    myChart.update();
}

まとめ

今回はChartJSで簡単にグラフを作ってリロード処理を実装してみました。今回は元々は別の問題を解説を行おうと思っていたのですが、最新版で修正されてしまっていたようで、再現しなかったため、急遽通常の解説となりました。

WordPressの記事内に、生のHTMLを記述することで、簡単なWebアプリをページ内に設置できることもわかって一石二鳥でした。

ChartJSの使い方がいまいちわからない、初回表示はできるけどデータの更新の仕方がわからないと言う方の参考になれば幸いです。

タイトルとURLをコピーしました