1. 記事一覧 >
  2. ブログ記事
category logo

【React】RechartsのBrushを使ってレンジスライダーを追加

(更新) (公開)

はじめに

React の Recharts を使って、グラフのレンジスライダーを作成しました。


レンジスライダーとは、以下の部分のことです。

レンジスライダー部分


こうやって、見たい範囲を指定したり、動かせます。

レンジスライダー 動作 動画


今回、これの実装方法について、書きたいと思います。


この記事に Recharts そのものの解説は有りません。

別記事

React TypeScript ESLint Prettier VSCode のプロジェクト作成

で、開発環境を作って、

Material UI(MUI) Recharts WebSocket FastAPIでリアルタイムグラフ描画

で、ダッシュボード内に Recharts がありますので、そのソースコードがスタート位置です。

具体的には、

https://github.com/mui/material-ui/tree/v5.8.7/docs/data/material/getting-started/templates/dashboard

Chart.tsx

がスタート位置です。


追加実装部分

いきりなり結論から書きますと、以下の部分を追加して、実現しました。

Chart.tsx
  <Brush
    dataKey="time"
    stroke="#448aff"
    height={30}
    startIndex={4}
    endIndex={6}
  >
    <AreaChart data={data}>
      <CartesianGrid strokeDasharray="3 3" />
      <Area
        type="monotone"
        dataKey="amount"
        stroke="none"
        fillOpacity={1}
        fill="#bdbdbd"
      />
    </AreaChart>
  </Brush>

ソースコード追加部分

Brush など、import に追加が必要でしたが、省略しています。


重要だったのは、以下の2点です。
1.<Brush /> によって、スライダーが出現する。
2.<Brush /> の子要素にチャートを指定すると、スライダーの背景のチャートになる。


特に 2 の方は、最初、気付かず、スライダーに重ねてチャートを配置して、大きさを合わせて...って、無駄な苦労をしていました。知ってしまえばなんてことなかったです。

以降、これを基本とし、続きは各パラメータの話になります。トラベラー(ドラッグできるハンドル部分)の形状を変更できたりします。


$ npm start

で、http://localhost:3000 にアクセスした結果の全体像です。

アクセスした結果の全体像

前述の通り、Material UI のダッシュボードを使っています。


Brush オプション

公式サイト(https://recharts.org/en-US/api/Brush) の内容を紐解きました。


dataKey

キーになる項目を指定します。今回の場合、
以下のようにキー : 値 を生成していて、time でスライドさせたいので、dataKey="time" です。

function createData(time: string, amount?: number) {
  return { time, amount };
}

const data = [
  createData('00:00', 0),
  createData('03:00', 300),
  createData('06:00', 600),
  createData('09:00', 800),
  createData('12:00', 1500),
  createData('15:00', 2000),
  createData('18:00', 2400),
  createData('21:00', 2400),
  createData('24:00', undefined),
];

x
レンジスライダーの配置位置 x 座標です。
x={100} の場合、以下のようになります。(親チャートの左端 が 0 です。正の値で右に移動します。指定しない場合、0 です。

レンジスライダーの配置位置 x 座標


y
レンジスライダーの配置位置 y 座標です。 y={0} の場合、以下のようになります。(親チャートの上端が 0 です。正の値で下に移動します。指定しない場合、親チャートのすぐ下に配置されます。

レンジスライダーの配置位置 y 座標


width
レンジスライダーの幅です。 width={200} の場合、以下のようになります。

レンジスライダーの幅


height
レンジスライダーの高さです。 height={10} の場合、以下のようになります。

レンジスライダーの高さ


data
<Brush /> コンポーネントが独自に持つデータ(Array)のようですが、用途が良く分かりませんでした。 チャートに無関係のデータを渡しても表示には影響しません。


travellerWidth
まず、トラベラー(traveller)とは、ドラッグできるハンドルのことです。
トラベラー(traveller)とは、ドラッグできるハンドルのこと


travellerWidth でトラベラーの幅を変更できます。
travellerWidth={30} の場合、以下のようになります。

トラベラーの幅を変更


traveller

公式サイトに載っていませんが、型定義を見ていたら、書いてあって、気になったので、試してみました。


React 要素を渡すか、要素を生成する関数を渡して、トラベラーの形状を変更できます。


トラベラーの形状のデフォルトは、以下のようです。

  static renderDefaultTraveller(props: any) {
    const { x, y, width, height, stroke } = props;
    const lineY = Math.floor(y + height / 2) - 1;

    return (
      <>
        <rect x={x} y={y} width={width} height={height} fill={stroke} stroke="none" />
        <line x1={x + 1} y1={lineY} x2={x + width - 1} y2={lineY} fill="none" stroke="#fff" />
        <line x1={x + 1} y1={lineY + 2} x2={x + width - 1} y2={lineY + 2} fill="none" stroke="#fff" />
      </>
    );
  }

これを、以下のように変更して渡してみます。

  function renderCustomTraveller(props: any) {
    const { x, y, width, height, stroke } = props;

    return (
      <rect x={x} y={y} width={width} height={height} fill={stroke} stroke="none" />
    );
  }
・・・
          <Brush
            dataKey="time"
            stroke="#448aff"
            traveller={renderCustomTraveller}
            height={30}
            startIndex={4}
            endIndex={6}
          >

結果、こうなります。(少しわかりにくいですが、白い部分が無くなりました。)

functionでトラベラーの形状を変更


直接要素指定の方は、以下のようになります。

  <Brush
    dataKey="time"
    stroke="#448aff"
    traveller={
      <svg>
        <rect
          width="3"
          height="15"
          fill="rgb(0,0,255)"
          stroke="rgb(0,0,0)"
        />
      </svg>
    }
    height={30}
    startIndex={4}
    endIndex={6}
  >

結果、こうなります。

直接要素指定でトラベラーの形状を変更


今回の場合、<svg> に以下の props が渡されて、React.cloneElement で適用されて、トラベラーの形状が決まって、<rect .../>によって、その中を描画しているという感じです。
【 svg の props 】
fill: '#fff'
height: 30
stroke: '#448aff'
width: 5
x : 444.5
y: 139


gap

2 にしたら、2 倍の移動距離、2 倍の範囲になるという意味でしょうか、gap={2} を指定すると、こうなります。

gap=2 2 倍の移動距離、2 倍の範囲


startIndex
endIndex
最初の選択範囲を指定できます。
startIndex={4} endIndex={6} で、4 目盛目(12:00)~ 6 目盛目(18:00)の範囲を選択した状態が初期状態です。(上記、gapオプションは指定無しです。)

startIndex,endIndex


tickFormatter
目盛りの表示をカスタマイズできます。
tickFormatter={(text, index) => `${text}-${index}`} とした場合、こうなります。

目盛りの表示をカスタマイズ


onChange
目盛りの範囲が変わるたびに呼び出されます。

  function handleBrushChange(newIndex: any) {
    console.log(newIndex);
  }
・・・
  <Brush
    dataKey="time"
    stroke="#448aff"
    height={30}
    startIndex={4}
    endIndex={6}
    onChange={handleBrushChange}
  >

のように、現在位置を取得できて、newIndex は、 {startIndex: 4, endIndex: 6} のような値です。


Brush 子要素について

<Brush /> の子要素にチャートを持ってくるとレンジスライダーの背景のチャートになります。 以下のような <AreaChart /> の子要素なのですが、これは、単体でもチャートとして機能するようなごく普通の書き方になります。 一応、説明を加えておきます。

<AreaChart data={data}>
  <CartesianGrid strokeDasharray="3 3" />
  <Area
    type="monotone"
    dataKey="amount"
    stroke="none"
    fillOpacity={1}
    fill="#bdbdbd"
  />
</AreaChart>

まず、<AreaChart data={data}> についてですが、data={data} の部分は、実際には、親要素のチャートのデータに引きずられて、描画には、反映されないようでした。
つまり、<AreaChart data={[]}> としても同じ結果になります。


<CartesianGrid strokeDasharray="3 3" />

点線の点の長さ<space>間隔 になります。


type="monotone"
なめらかなカーブを描いた形になります。


dataKey="amount"
親要素チャートの dataKey に合わせます。指定しなかったり、適当に指定はできません。


stroke="none"
縁取りする線の色を指定できます。 "none" は線無しです。 stroke="red" とすると、以下のようになります。

stroke=red


fillOpacity={1}
塗りつぶしの透過度になります。


fill="#bdbdbd"
塗りつぶす色です。

<AreaChart /> のデフォルト値は、以下のようになっています。(透過度 0.6 の青っぽい色)

  static defaultProps = {
    stroke: '#3182bd',
    fill: '#3182bd',
    fillOpacity: 0.6,
    xAxisId: 0,
    yAxisId: 0,
    legendType: 'line',
    connectNulls: false,
    // points of area
    points: [] as AreaPointItem[],
    dot: false,
    activeDot: true,
    hide: false,

    isAnimationActive: !Global.isSsr,
    animationBegin: 0,
    animationDuration: 1500,
    animationEasing: 'ease',
  };

小さくて少しわかりにくいですが、<defs><linearGradient...>...</linearGradient></defs> で以下のようにグラデーション調にもできます。

<AreaChart data={[]}>
  <defs>
    <linearGradient id="colorUv" x1="0" y1="0" x2="0" y2="1">
      <stop offset="5%" stopColor="#8884d8" stopOpacity={0.8} />
      <stop offset="95%" stopColor="#8884d8" stopOpacity={0} />
    </linearGradient>
  </defs>
  <CartesianGrid strokeDasharray="3 3" />
  <Area
    type="monotone"
    dataKey="amount"
    stroke="#8884d8"
    fillOpacity={1}
    fill="url(#colorUv)"
  />
</AreaChart>

defs linearGradient でグラデーション


以上!!

loading...