Chart.js3.x系でグラフ上のクリック位置に値をセットする方法

技術備忘録

執筆時点でChart.jsは3.5.1となりました。以前書いたグラフ上のクリック位置に値をセットする方法はChart.js2.x系での実装だったので、今回はChart.js3.x系に書き換えた方法について説明したいと思います。

Chart.js2系から3系では記述方法がかなり変わっているのでバージョンを上げる際は注意が必要です。

Chart.js 2系での実装についての説明はこちら

スポンサーリンク

デモ

グラフ上をクリックすることによってクリック位置に棒グラフを移動させることが出来ます。

また、Click meボタンは親コンポーネントのstateを更新時に子コンポーネントであるグラフコンポーネントがどのような挙動をするのか確認するために設置したボタンです。

スポンサーリンク

ソースコード

Chart.jsのラッパーとしてreact-chartjs-2を使用しています。

import React, { useState } from 'react'
import ReactDOM from 'react-dom'
import {Bar} from 'react-chartjs-2';

const App = () => {
    const [data, setData] = useState([65, 59, 80, 81, 56, 55, 40])
    const [count, setCount] = useState(0)
    const setNewData = (x,y) => {
        data[x] = y
        setData(data)
    }
    return (
        <>
        <div>
            <BarInput data={data} setNewData={setNewData}/>
        </div>
        <button onClick={()=>setCount(count + 1)}>Click me</button>
        {count}
        </>
    )
}

const BarInput = (props) => {
    const labels = props.data.map((d,i)=>{return `data:${i}`})
    const data = {
    labels: labels,
    datasets: [{
        label: 'demo',
        data: props.data,
        backgroundColor: [
            'rgba(255, 99, 132, 0.2)',
            'rgba(255, 159, 64, 0.2)',
            'rgba(255, 205, 86, 0.2)',
            'rgba(75, 192, 192, 0.2)',
            'rgba(54, 162, 235, 0.2)',
            'rgba(153, 102, 255, 0.2)',
            'rgba(201, 203, 207, 0.2)'
        ],
        borderColor: [
            'rgb(255, 99, 132)',
            'rgb(255, 159, 64)',
            'rgb(255, 205, 86)',
            'rgb(75, 192, 192)',
            'rgb(54, 162, 235)',
            'rgb(153, 102, 255)',
            'rgb(201, 203, 207)'
        ],
        borderWidth: 1
    }]
    };
    const options = {
        scales: {
            yAxis: {
                max: 100,
                min: 0,
                beginAtZero: true
            }
        },
        plugins: {
            legend: {
                display: false
            }
        },
        onClick: function(evt, elements, chartInstance) {
            let x = chartInstance.scales.x.getValueForPixel(evt.native.clientX - chartInstance.canvas.getBoundingClientRect().left)
            let y = chartInstance.scales.yAxis.getValueForPixel(evt.native.clientY - chartInstance.canvas.getBoundingClientRect().top)
            chartInstance.data.datasets[0].data[x] = y
            chartInstance.update()
            props.setNewData(x, y) // 親側でpropsを変更させる
        },
        maintainAspectRatio: false
    }
    console.log('chart render')
    return (
        <Bar
            height={300}
            data={data} options={options}/>
    )
}

ReactDOM.render(
    <App />,
    document.getElementById('root')
);
スポンサーリンク

Chart.js2系から3系に移行時の変更点

options.onClick

{
    ...
    onClick: function(evt){
        let chartInstance = chartReference.current.chartInstance
        let x = chartInstance.scales["x-axis-0"].getValueForPixel(evt.clientX-chartInstance.canvas.getBoundingClientRect().left)
        let y = chartInstance.scales["y-axis-0"].getValueForPixel(evt.clientY-chartInstance.canvas.getBoundingClientRect().top)
        chartInstance.data.datasets[dataIndex].data[x] = y
        chartInstance.update()
        props.setNewData(x, y) // 親側でpropsを変更させる
    },
}
{
    ...
    onClick: function(evt, elements, chartInstance) {
        let x = chartInstance.scales.x.getValueForPixel(evt.native.clientX - chartInstance.canvas.getBoundingClientRect().left)
        let y = chartInstance.scales.yAxis.getValueForPixel(evt.native.clientY - chartInstance.canvas.getBoundingClientRect().top)
        chartInstance.data.datasets[0].data[x] = y
        chartInstance.update()
        props.setNewData(x, y)// 親側でpropsを変更させる
    },
}

Chart.js 2系と3系でのonClickの書き方の違いをまとめると以下の通りです。

  • onClickの第3引数にchartインスタンスが含まれるようになった(公式
  • x軸、y軸のデフォルトのidが変化した(上の例ではy軸はyAxisでoptions内で定義)
  • クリックイベントからクリック位置を取得する方法がevt.clientXからevt.native.clientXになった

options.legend

{
    ...
    legend: {
        display: false
    },
}
{
    ...
    plugins: {
        legend: {
            display: false
        }
    },
}

Chart.js 3系から凡例(legend)はoptions.pluginsで設定するようになりました。そのほかにもtitleやtooltipもpluginsの中に移動したようです(公式

redrawが必要なくなった

Chart.js 2系では再レンダーする場合はredraw (react-chartjs-2内の変数)が必要でした。redrawを設定することで react-chartjs-2 でdestroy()が自動的に行われます。

Chart.js 3系では再レンダーを簡単に行うことができるようになりました。そのため、propsが変化時にもredrawを設定しなくても大丈夫です。

これを試すため、デモ内にはClick meボタンを設置しています。このボタンをクリックすることで、グラフコンポーネントの親コンポーネントでのstateが変化します。

Chart.js 2.xでredrawを設定していると親コンポーネントのstateが変化したときにグラフコンポーネントが再レンダーされるのですが、destroy()が呼ばれるため、stateの更新ごとにグラフが一度消えてから下から現れて表示されるようになってしまいました。これは見栄え的にあまりよくありませんでした。

スポンサーリンク

現状の実装での問題点

デモを触るとわかると思いますが、グラフ内の棒グラフ本体をクリックすると変化後の棒グラフの色が灰色になってしまいます。現状この原因はよくわかっていないのですが、見栄えが良くないので、今後修正をしていきたいと思います。(Chart.js 3.x由来の問題??)

コメント

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