Chart.jsでグラフの色を途中で変える

## 目的

折れ線グラフの色を途中で変えたい場合がありますよね。
Chart.jsでの実現方法をまとめます。

## 例

ここでは消費税収額を題材にします。
普通にグラフを作成すると以下のとおりです。消費**税率**は上昇しているので、税率と税収の関係がわかるように、税率ごとに色分けしたくなりますね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614119/01e2acf6-f7b3-102b-9455-4c9a11da5e29.png)

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Chart</title>
    <script src="https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js"></script>
  </head>
  <body>
    <canvas id="myChart"></canvas>
    <script>
        const data = [3.3, 4.6, 5.0, 5.2, 5.6, 5.6, 5.8, 6.1, 9.3, 10.1,
                    10.4, 9.8, 9.8, 9.5, 9.7, 10.0, 10.6, 10.5, 10.3, 10.0,
                    9.8, 10.0, 10.2, 10.4, 10.8, 16.0, 17.4, 17.2,17.5, 17.7, 
                    18.4, 21.0, 21.9, 22.2, 23.4];
        const labels = [];
        for (let i = 0; i < data.length; ++i) {
            labels.push((i+1989).toString());
        }
        let lineCtx = document.getElementById("myChart");
        let lineConfig = {
          type: 'line',
          data: {
            labels: labels,
            datasets: [{
              data: data,
              fill: true,
              backgroundColor: 'rgba(252,128,67,0.4)',
              borderColor: 'rgba(252,128,67,1)',
            }, 
          },
          options:{
            scales: {
                x: {
                    display: true,
                    title: {
                        display: true,
                        text: '年'
                    }
                },
                y: {
                    display: true,
                    title: {
                        display: true,
                        text: '消費税収 / 兆円'
                    },
                }
            }
          }
        };
        let lineChart = new Chart(lineCtx, lineConfig);
    </script>
  </body>
</html>
```


## 解決策
消費税率は3%から5%(1997年), 8%(2014年), 10%(2019年)と上がっています。

この場合はグラフを４本描画し、該当しない区間は**NaN**で埋めればOKです。
上のコードのスクリプト部分を次のように変更します。

```javascript
/*
元データ
const data = [3.3, 4.6, 5.0, 5.2, 5.6, 5.6, 5.8, 6.1, 9.3, 10.1, 
                10.4, 9.8, 9.8, 9.5, 9.7, 10.0, 10.6, 10.5, 10.3, 10.0, 
                9.8, 10.0, 10.2, 10.4, 10.8, 16.0, 17.4, 17.2, 17.5, 17.7, 
                18.4, 21.0, 21.9, 22.2, 23.4];
*/

// 元データを４つに分割
const data1 = [3.3, 4.6, 5.0, 5.2, 5.6, 5.6, 5.8, 6.1, 9.3, NaN, 
            NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 
            NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 
            NaN, NaN, NaN, NaN, NaN]

const data2 = [NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 9.3, 10.1,
            10.4, 9.8, 9.8, 9.5, 9.7, 10.0, 10.6, 10.5, 10.3, 10.0, 
            9.8, 10, 10.2, 10.4, 10.8, 16.0, NaN, NaN, NaN, NaN, 
            NaN, NaN, NaN, NaN, NaN]

const data3 = [NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 
            NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 
            NaN, NaN, NaN, NaN, NaN, 16.0, 17.4, 17.2, 17.5, 17.7, 
            18.4, NaN, NaN, NaN, NaN]

const data4 = [NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 
            NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 
            NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 
            18.4, 21.0, 21.9, 22.2, 23.4]


const labels = [];
for (let i = 0; i < data1.length; ++i) {
    labels.push((i+1989).toString());
}
let lineCtx = document.getElementById("myChart");
let lineConfig = {
  type: 'line',
  data: {
    labels: labels,
    // 4つのグラフを作成
    datasets: [{
      label: '税率3%',
      data: data1,
      fill: true,
      backgroundColor: 'rgba(252,128,67,0.4)',
      borderColor: 'rgba(252,128,67,1)',
    }, {
      label: '税率5%',
      data: data2,
      fill: true,
      backgroundColor: 'rgba(75,192,192,0.4)',
      borderColor: 'rgba(75,192,192,1)',
    }, {
      label: '税率8%',
      data: data3,
      fill: true,
      backgroundColor: 'rgba(233,196,106,0.4)',
      borderColor: 'rgba(233,196,106,1)',
    }, {
      label: '税率10%(軽減税率8%)',
      data: data4,
      fill: true,
      backgroundColor: 'rgba(125,125,125,0.4)',
      borderColor: 'rgba(125,125,125,1)',
    }, 
    ],
  },
  options:{
    scales: {
        x: {
            display: true,
            title: {
                display: true,
                text: '年'
            }
        },
        y: {
            display: true,
            title: {
                display: true,
                text: '消費税収 / 兆円'
            },
        }
    }
  }
};
let lineChart = new Chart(lineCtx, lineConfig);

```


次のようにグラフが作成されます。税率変化のタイミングで税収が伸びていることがよく分かります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614119/45972a31-e1d8-2317-9e8b-91542b9a60a6.png)


上のコードでは、わかりやすくするため直接NaNに置き換えた4つのデータを記載しました。
たとえば次のようにして、切り替えるタイミングを指定して上記のデータを作成できます。

```javascript
const points = [1989, 1997, 2014, 2019, 2023].map(a => a-1989);
const data = [3.3, 4.6, 5.0, 5.2, 5.6, 5.6, 5.8, 6.1, 9.3, 10.1, 10.4, 9.8,
            9.8, 9.5, 9.7, 10.0, 10.6, 10.5, 10.3, 10.0, 9.8, 10.0, 10.2, 10.4,
            10.8, 16.0, 17.4, 17.2,17.5, 17.7, 18.4, 21.0, 21.9, 22.2, 23.4];

function generateOutput(data, points) {
    const output = [];
    for (let i=0; i< points.length-1; i++){
        let dataset = [];
        for (let j=0; j< data.length; j++){
            if((points[i] <= j)&&(points[i+1] >= j)){
                dataset.push(data[j])
            } else {
                dataset.push(NaN)
            }
        }
        output.push(dataset)
    }
    return output
}
```

## まとめ

複数のグラフを違う色で描画して一本に見せることで実現できました。

[某配信サービスでのコメント推移を可視化するサイト](https://reyapps.link/)を作成時に知りました。もしよければご覧ください。

## 利用したデータ

https://www.mof.go.jp/tax_policy/summary/condition/a03.htm