
Published: January 15, 2025 at 3:08 AM
By: Emma Schaale
D3とReactで作るシンプルな棒グラフ
Reactはフロントエンド開発でおなじみですが、D3.jsと組み合わせてカスタムチャートを作ったことはありますか?「難しそう…」と思うかもしれませんが、意外と簡単なんです😊。
今日はこういう感じの棒グラフを作ります:
この記事では、Reactのコンポーネント設計を活かしながら、D3でデータを解析し、宣言的な記述で棒グラフを作る方法をご紹介します📊。宣言的な書き方で、コードの可読性やメンテナンス性を高めましょう!
この記事でわかること 🌟
- Reactのコンポーネント設計を活用して整理されたコードを作る方法
- D3によるデータ解析やスケーリングの基本
- 宣言的な記述で効率的にチャートを作成するコツ
簡単な手順で、シンプルかつ直感的なチャートを作成してみませんか?✨
プロジェクトのセットアップ 🛠️
まずはプロジェクトのセットアップから始めましょう。VS Codeやお好みのコードエディタを開いて、ターミナルを開きます。そして、以下のコマンドを実行してプロジェクトを作成してください。
npx create-next-app@latest
プロンプトが表示されたら、すべてデフォルトのオプションを選択してください。これで、Next.jsプロジェクトが自動的にセットアップされます🎉。次に、プロジェクトディレクトリに移動して、必要なパッケージをインストールします:
npm install d3
準備完了!
これで、プロジェクト作業を始める準備が整いました。まずは、src/app
フォルダ内の page.tsx
ファイルを開いてみましょう。このファイルは以下のスクリーンショットのような内容になっているはずです:
次に、デフォルトで含まれているコンテンツを削除します。そして、以下のコードを 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.tsx
の Home()
関数内に追加します。以下のデータを 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
プロパティを使って、チャートの innerWidth
と innerHeight
(この部分は軸のラベルや軸のタイトルが配置される部分です)を計算します。イメージしやすいように、こちらの画像を参考にしてください:
これで、innerWidth
と innerHeight
を計算するコードを追加します。難しくないですよ!
const innerWidth = width - margin.right - margin.left;
const innerHeight = height - margin.top - margin.bottom;
g
要素の追加
次に、チャートの内側のコンテナとして機能する g
要素を追加します。この要素を margin.left
と margin.top
に従って変換(transform)し、チャートの幅と高さの制限を innerWidth
と innerHeight
に設定します。
以下のコードを追加してください:
<g
transform={`translate(${margin.left}, ${margin.top})`}
width={innerWidth}
height={innerHeight}
>
これで、チャート内のすべての要素を整理された位置に描画できるようになります!
xScaleとyScale関数の作成📏
次に、xScale
と yScale
の関数を作成します。これらの関数は、データセットに含まれる生徒の名前とテストのスコアを受け取り、それをチャートのサイズにスケーリングしてフィットさせる役割を持ちます。
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
グループ要素を作成します。各バーが見えるように、x
と y
の位置、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"
/>
))}
バーが描けました!:
バーにテキストラベルを追加📝
次に、各バーにテキストラベルを追加します。このテキストもバーと同様に、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>
名前のラベルを追加すると、こういう感じになります:
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>
);
};
注意点: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>
);
このようにすることで、Y軸のラベルが正しい順序で表示されるようになります。
グラフにタイトルを追加✨
最後に、このグラフにタイトルを追加しましょう。🎉
位置の指定📍
タイトルはグラフの中央上部に配置します。タイトルはチャート領域の外側にあるため、innerHeight
や innerWidth
を基準にしたグループ要素の外側に追加する必要があります。
コード例
以下のコードを 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>
こういう感じになります。これで終わりです!いかがでしょうか?
まとめ
意外と簡単だったのではないでしょうか?😊 ここまでの手順をすべて実行すると、Reactのコンポーネントベースの設計とD3のロジックを組み合わせて、直感的で美しいチャートを作成する方法が分かったかと思います。
GitHubリポジトリはこちらからご覧いただけます。また、ライブサイトはこちらのリンクで見えます。
このチュートリアルが役立ち、ReactとD3を活用した宣言的なチャート作成について新しい視点を提供できたら嬉しいです。
質問やコメントがあれば、ぜひお気軽にお知らせくださいね! ✨