Developersをフォローする

【React】react-chartjs-2を使用して理想の体重をグラフ化してみた

フロントエンド

完全に私事ですが2022年4月に体重100kgの壁を突破し、現在、適度な運動と食事を気を付けるようにしています。(2022/08月時点では95kg)
ただ、自身の適正体重や目指している体重が本当に適正であるのか、簡単に可視化できるツールが見当たらなかったので作成しようと考えました。
そこで今回、react-chartjs-2を使用した理想の体重計算ツールを作成することにいたしました。

使用技術/ライブラリ等

  • Next.js / TypeScript
  • react-chartjs-2
  • MUI
  • formik/yup
  • Vercel

理想の体重計算ツールの紹介

URL:https://ideal-body-weight.vercel.app/
操作としては身長と体重を入力し、計算ボタンをクリックすることでグラフが表示されるだけのシンプルなものになってます。
身長と体重の入力から計算した項目は下記になります。

  • 適正体重
  • 美容体重
  • シンデレラ体重
  • BMI

それぞれの項目に関して詳しいことは、説明を省きますが気になる方は調べてみてください。
美容体重とシンデレラ体重はネタとして一応計算してみました。
実際に計算した結果が下記のピクチャのようになります。

現在の体重と適正体重にかなりの差があることがわかります。
これはまずい。。。

技術的な話

今回のメインとしては、グラフ表示に使用したreact-chartjs-2になるので、その他については簡単にまとめたいと思います。

入力フォームやグラフの大枠のレイアウト、ボタンはMUIを使用して実装してあります。
フォームの処理に関してはformik/yupを使用して簡単なバリデーション等を実装しています。
計算処理については、カスタムフックを実装し計算処理を行っています。
デプロイに関しては、Vercelに丸投げしてます。(GitHubのリポジトリを登録するだけなので。。)

ちなみに計算処理は下記になります。
身長と体重を受け取ってそれぞれの値を計算して、少数第一までを返しているシンプルな処理になります。

export const useCalculationFuncs = () => {
  const idealBodyWeight = useCallback((height: number, weight: number) => {
    const heightSquared = (height / 100) ** 2;
    const bmi = weight / heightSquared;
    const appropriateWeight = heightSquared * 22;
    const cosmeticWeight = heightSquared * 20;
    const cinderellaWeight = heightSquared * 18;

    return {
      bmi: bmi.toFixed(1),
      appropriateWeight: appropriateWeight.toFixed(1),
      cosmeticWeight: cosmeticWeight.toFixed(1),
      cinderellaWeight: cinderellaWeight.toFixed(1),
    };
  }, []);

  return { idealBodyWeight };
};

グラフ表示について

今回使用したグラフは、Vertical Bar Chartという棒グラフのものになります。
基本的にグラフ表示させたいデータと全体的なスタイルを調整するオプションを用意することで、簡単にグラフの表示を行うことができます。
流れとしては下記のようになります。

  1. 使用したいグラフコンポーネントをインポート
  2. インポートしたコンポーネントにグラフ表示させたいデータとオプションを渡す

今回のオプションの設定に関して、工夫した点としてはy軸を固定したかったので最小値と最大値を設定しています。

const options = {
  cornerRadius: 20,
  layout: { padding: 0 },
  legend: { display: false },
  maintainAspectRatio: false,
  responsive: true,
  xAxes: [
    {
      ticks: {
        fontColor: '#65748B',
      },
      gridLines: {
        display: false,
        drawBorder: false,
      },
    },
  ],
  yAxes: [
    {
      ticks: {
        fontColor: '#65748B',
        beginAtZero: true,
        min: 0,
      },
      gridLines: {
        borderDash: [2],
        borderDashOffset: [2],
        color: '#E6E8F0',
        drawBorder: false,
        zeroLineBorderDash: [2],
        zeroLineBorderDashOffset: [2],
        zeroLineColor: '#E6E8F0',
      },
    },
  ],
  tooltips: {
    backgroundColor: '#FFFFFF',
    bodyFontColor: '#65748B',
    borderColor: '#E6E8F0',
    borderWidth: 1,
    enabled: true,
    footerFontColor: '#65748B',
    intersect: false,
    mode: 'index',
    titleFontColor: '#121828',
  },
  // ここでy軸の固定を行う
  scales: {
    y: {
      min: 0,
      max: 120,
    },
  },
};

最小値を0、最大値を120で設定しy軸を固定しています。
最大値120かよ!足りねーよ!という方がいましたらすみません!

実際に表示するデータに関しては、先ほどの計算処理の結果を配列として渡してあげてます。

const labels = [
  '現在の体重',
  '適正体重',
  '美容体重',
  'シンデレラ体重',
];
// ... 一部省略
  const data = useMemo(() => {
    // 計算結果を配列にする
    const bodyInfoData = [currentWeight, appropriateWeight, cosmeticWeight, cinderellaWeight];
    return {
      labels,
      datasets: [
        {
          backgroundColor: '#3F51B5',
          barPercentage: 0.5,
          barThickness: 12,
          borderRadius: 4,
          categoryPercentage: 0.5,
          data: bodyInfoData,
          label: '体重',
          maxBarThickness: 10,
        },
      ],
    };
  }, [currentWeight, appropriateWeight, cosmeticWeight, cinderellaWeight]);

今回は比較する対象が入力者自身のみなので、シンプルな構成ですが`datasets`の箇所に複数のデータを渡すことも可能なのでユーザー間で何かしらのデータを比較する際に使用すると面白いと思いました。
今回は棒グラフの表示ですが、他にも折れ線グラフや円グラフ、レーダーチャートなど様々なグラフ表示を行えるみたいなので1年間の体重の変化等を表示することができたら面白そうと感じました。

最後に

react-chartjs-2を使用して簡単なグラフを表示させてみました。
理想の体重も可視化できたところなので、適度な運動と無理のない食事制限を頑張りたいと思います。