D3とReactで作るシンプルな棒グラフ

Published: January 15, 2025 at 3:08 AM

By: Emma Schaale

D3とReactで作るシンプルな棒グラフ

Reactはフロントエンド開発でおなじみですが、D3.jsと組み合わせてカスタムチャートを作ったことはありますか?「難しそう…」と思うかもしれませんが、意外と簡単なんです😊。

今日はこういう感じの棒グラフを作ります: chart-done

この記事では、Reactのコンポーネント設計を活かしながら、D3でデータを解析し、宣言的な記述で棒グラフを作る方法をご紹介します📊。宣言的な書き方で、コードの可読性やメンテナンス性を高めましょう!

この記事でわかること 🌟

  • Reactのコンポーネント設計を活用して整理されたコードを作る方法
  • D3によるデータ解析やスケーリングの基本
  • 宣言的な記述で効率的にチャートを作成するコツ

簡単な手順で、シンプルかつ直感的なチャートを作成してみませんか?✨

プロジェクトのセットアップ 🛠️

まずはプロジェクトのセットアップから始めましょう。VS Codeやお好みのコードエディタを開いて、ターミナルを開きます。そして、以下のコマンドを実行してプロジェクトを作成してください。

npx create-next-app@latest

プロンプトが表示されたら、すべてデフォルトのオプションを選択してください。これで、Next.jsプロジェクトが自動的にセットアップされます🎉。次に、プロジェクトディレクトリに移動して、必要なパッケージをインストールします:

npm install d3

準備完了!

これで、プロジェクト作業を始める準備が整いました。まずは、src/appフォルダ内の page.tsxファイルを開いてみましょう。このファイルは以下のスクリーンショットのような内容になっているはずです:

Chart Explanation

次に、デフォルトで含まれているコンテンツを削除します。そして、以下のコードを page.tsxにコピー&ペーストしてください:

export default function Home() {
  return (
    <div>
      <main></main>
    </div>
  );
}

さらに、ページ全体にデフォルトのスタイルを追加してみましょう。これはチャートをスタイリングするためではなく、ページ全体に基本的なスタイルを適用するためのものです。このスタイルは src/app/globals.cssファイルに追加してください。

html,
body {
  max-width: 100vw;
  overflow-x: hidden;
}

body {
  color: #ffffff;
  background: #171717;
  font-family: Arial, Helvetica, sans-serif;
  padding: 80px;
}

* {
  box-sizing: border-box;
  padding: 0;
  margin: 0;
}

サンプルデータセットの追加

次に、サンプルデータセットを page.tsxHome()関数内に追加します。以下のデータを Home()関数の中に追加してください:

const data = [
    { name: "Miya", score: 3 },
    { name: "Aoki", score: 8 },
    { name: "Kawabata", score: 12 },
    { name: "Takeo", score: 9 },
    { name: "Shimizu", score: 6 },
  ];

これで、グラフを作成するために必要なデータが準備できました! 次に、このデータを使ってチャートを描画していきます。

SVGコンテナの作成🖼️

次に、svg要素をDOMに追加し、幅と高さを設定します。これらの属性は外部からインスタンス化できるようにプロパティとして定義します。

<svg width={width} height={height}></svg>

まず、チャートの高さ、そしてマージンのプロパティを追加しましょう。これにより、チャートのサイズや配置が調整できます📏。

 const width = 800;
  const height = 800;
  const margin = { top: 30, right: 30, bottom: 60, left: 60 };

次に、marginプロパティを使って、チャートの innerWidthinnerHeight(この部分は軸のラベルや軸のタイトルが配置される部分です)を計算します。イメージしやすいように、こちらの画像を参考にしてください:

ChartExplanation

これで、innerWidthinnerHeightを計算するコードを追加します。難しくないですよ!

 const innerWidth = width - margin.right - margin.left;
  const innerHeight = height - margin.top - margin.bottom;

g要素の追加

次に、チャートの内側のコンテナとして機能する g要素を追加します。この要素を margin.leftmargin.topに従って変換(transform)し、チャートの幅と高さの制限を innerWidthinnerHeightに設定します。

以下のコードを追加してください:

<g
  transform={`translate(${margin.left}, ${margin.top})`}
  width={innerWidth}
  height={innerHeight}
>

これで、チャート内のすべての要素を整理された位置に描画できるようになります!

xScaleとyScale関数の作成📏

次に、xScaleyScaleの関数を作成します。これらの関数は、データセットに含まれる生徒の名前とテストのスコアを受け取り、それをチャートのサイズにスケーリングしてフィットさせる役割を持ちます。

xScaleの作成

まず、xScaleでは、データセット内の各名前を受け取り、それを 0から innerWidthの範囲に設定します。以下のコードを使用してください:

const xScale = d3
  .scaleBand()
  .domain(data.map((d) => d.name))
  .range([0, innerWidth])
  .padding(0.1);

これで、xScaleが設定され、各名前が横軸に適切に配置される準備が整いました!🎯

yScaleの作成

次に、yScaleでは、テストスコアを受け取り、チャートの高さに合わせてスケーリングします。以下のコードを追加してください:

const yScale = d3.scaleLinear().domain([0, 14]).range([0, innerHeight]);

この設定により、テストスコア(0から12)がチャート内で正しい位置に表示されるようになります。

棒グラフのメイン部分:バーの描画📊

次に、チャートの中心となる部分、つまりバーを作成していきます。データの各要素を反復処理し、それぞれの rect(長方形)SVG要素を包む gグループ要素を作成します。各バーが見えるように、xyの位置、fill(塗り色)、width(幅)、height(高さ)を設定します。以下のコードを g要素の中に追加してください:

{data.map((d, i) => (
 <g key={i}>
                <rect
                  x={xScale(d.name)}
                  y={innerHeight - yScale(d.score)}
                  fill="cornflowerblue"
                  width={xScale.bandwidth()}
                  height={yScale(d.score)}
                  stroke="lightblue"
                  strokeWidth={1.5}
                  role="tooltip"
                />
))}

バーが描けました!:

chart-wout-labels

バーにテキストラベルを追加📝

次に、各バーにテキストラベルを追加します。このテキストもバーと同様に、map関数内で text要素として描画します。

x座標の設定

テキストの x座標は、名前の xScale値に xScale.bandwidth()(各バーの幅)を2で割った値を加算して、ラベルをバーの中央に配置します。また、textAnchorプロパティを middleに設定することで、テキストがバーの中央揃えになるように調整します。

y座標の設定

テキストの y座標は、チャートの innerHeightに一定の値(例: 40)を加算して、バーの下に配置されるようにします。

以下がそのコード例です:

<text
  x={xScale(d.name) + xScale.bandwidth() / 2}
  y={innerHeight + 40}
  textAnchor="middle"
  fontSize={24}
  fill="white"
>
  {d.name}
</text>

名前のラベルを追加すると、こういう感じになります:

chart-names

Y軸の追加

最後に、Y軸を追加します。このために、専用のコンポーネントを作成します。

Y軸では、以下のプロパティを使用します:

  • yScale関数
  • innerWidth
  • innerHeight
  • オプションの tickSizeプロパティ(デフォルト値は 6

まず、新しい YAxis.tsxコンポーネントを page.tsxと同じフォルダ内に作成します。以下のコードを追加してください:

type YAxisProps = { 
  yScale: ScaleLinear<number, number>; 
  innerWidth: number;
  innerHeight: number;
  tickSize?: number;
};

export const YAxis = ({ 
  yScale, 
  innerWidth,
  innerHeight,
  tickSize = 4,
}: YAxisProps) => {
  return (<g></g>);
}

export default YAxis;


軸線を追加

次に、Y軸のグループ内にメインの軸線を追加します:

<line
  x1={0}
  x2={0}
  y1={0}
  y2={innerHeight}
  stroke="lightgray"
  strokeWidth={1}
/>

ティック(目盛り)を追加

Y軸を完成させるには、バーごとのティックと補助線を追加します。page.tsxコンポーネントのバーと同様に、yScale.ticks()を使用してティックを反復処理します。

以下のコードを使用してください:

export const YAxis = ({
  yScale,
  innerWidth,
  innerHeight,
  tickSize = 6,
}: YAxisProps) => {
  const ticks = yScale.ticks(tickSize);
  console.log('ticks', ticks);

  return (
    <g>
      <line
        x1={0}
        x2={0}
        y1={0}
        y2={innerHeight}
        stroke="lightgray"
        strokeWidth={1}
      ></line>

      {ticks.map((tick, index) => {
        const y = yScale(tick);
        return (
          <g key={index}>
            <line
              x1={0}
              x2={innerWidth}
              y1={innerHeight - y}
              y2={innerHeight - y}
              stroke="gray"
              strokeWidth={1}
            />
            <text
              x={-10}
              y={innerHeight - y}
              textAnchor="end"
              fontSize={12}
              fill="lightgray"
            >
              {tick}
            </text>
          </g>
        );
      })}
    </g>
  );
};

chart-lined-wrong

注意点:Y軸ラベルの反転⚠️

初期状態では、Y軸ラベルが上から下に向かって低い値から高い値に並んでしまいます。これを修正するには、yの位置を反転させる必要があります:

return (
  <g key={index}>
    <line
      x1={0}
      x2={innerWidth}
      y1={innerHeight - y}
      y2={innerHeight - y}
      stroke="gray"
      strokeWidth={1}
    />
    <text
      x={-10}
      y={innerHeight - y}
      textAnchor="end"
      fontSize={12}
      fill="lightgray"
    >
      {tick}
    </text>
  </g>
);

chart-lined-right

このようにすることで、Y軸のラベルが正しい順序で表示されるようになります。

グラフにタイトルを追加✨

最後に、このグラフにタイトルを追加しましょう。🎉

位置の指定📍

タイトルはグラフの中央上部に配置します。タイトルはチャート領域の外側にあるため、innerHeightinnerWidthを基準にしたグループ要素の外側に追加する必要があります。

コード例

以下のコードを page.tsxファイルに追加してください:

<svg width={width} height={height}>
  <text
    x={innerWidth / 2 + 30}
    y={25}
    textAnchor="middle"
    fontSize={24}
    fill="white"
  >
    生徒たちのテストスコア
  </text>
  <g
    transform={`translate(${margin.left}, ${margin.top})`}
    width={innerWidth}
    height={innerHeight}
  >
    {/* ... */}
  </g>
</svg>

こういう感じになります。これで終わりです!いかがでしょうか?

chart-done

まとめ

意外と簡単だったのではないでしょうか?😊 ここまでの手順をすべて実行すると、Reactのコンポーネントベースの設計とD3のロジックを組み合わせて、直感的で美しいチャートを作成する方法が分かったかと思います。

GitHubリポジトリはこちらからご覧いただけます。また、ライブサイトはこちらのリンクで見えます。

このチュートリアルが役立ち、ReactとD3を活用した宣言的なチャート作成について新しい視点を提供できたら嬉しいです。

質問やコメントがあれば、ぜひお気軽にお知らせくださいね! ✨