clip-pathでd3グラフの指定した範囲の色を変える

### まえがき
d3でグラフを作成しているとき、指定した範囲のグラフ部分の色を変更したい場面があった。実際のイメージはこんな感じ。
![スクリーンショット 2020-01-07 15.00.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547300/5ccc471f-0958-5c92-b4d5-6344eb817175.png)

グラフのうち、値が100以上の部分は赤、それ以下は青で描画させる。

ところが、d3自体にはこれを簡単にやってくれる機能がないらしい。そこで、clip-pathというSVGのプロパティを使ってこれを実現する。

## clip-pathとは
まず、clip-pathについて理解しておく必要がある。clip-pathを使用すれば、他の場所で定義された一部分を取り除くことができる。これをクリッピングと呼ぶ。

**使用例1)**
![スクリーンショット 2020-01-07 1.42.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547300/f5183a9e-8ac2-c415-1d4f-a19ba413ebfb.png)
コードは以下。

```html
      <svg>
        <clipPath id="clip-left-above">
          <rect x="0" y="0" width="130" height="50" />　//切り取る部分の設定
        </clipPath>
        <circle cx="100" cy="100" r="100" clip-path="url(#clip-left-above)" /> //元の図形
      </svg>
```

まず、clipPathタグに固有のidを指定したのち、そのタグ内で、切り取る部分（実際に描画させたい部分）を指定している。そして、元の図形(切り取りして描画させたい図形)のタグにclip-path属性を"url(#先程指定したclipPathのid)"の形で付与する。

すると、5行目で書かれている、中心が（cx, cy) = (100, 100)で半径100の円のうち、clipPath内で指定したrectと重なる部分のみが実際に画面に描画される。

**使用例2)**
![スクリーンショット 2020-01-07 2.39.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547300/cb8c28f0-6667-67e1-c40e-b9e806a8469f.png)

コードは以下。

```html
<svg width="1000" height="1000">
        <clipPath id="clip-somewhere">
          <rect x="130" y="0" width="125" height="200" />
          <rect x="350" y="20" width="255" height="180" />
          <rect x="220" y="220" width="120" height="110" />
          <rect x="400" y="200" width="170" height="155" />
          <rect x="130" y="350" width="190" height="110" />
          <rect x="430" y="440" width="220" height="50" />
          <rect x="145" y="515" width="450" height="40" />
          <rect x="222" y="570" width="160" height="100" />
          <rect x="490" y="560" width="230" height="230" />
        </clipPath>
        <image xlinkHref={"画像のURL"}  width="800" height="800" clip-path="url(#clip-somewhere)" />
      </svg>
```

何と、clip-pathを使えばこんなことまでできてしまう。画像が全部みたい？であれば、上記のコードを使って、ぜひ自分でやってみると良い。見たい部分をclipPathのタグ内で好きな形で範囲指定できる。

## d3グラフで指定の範囲の色を変える
おふざけはここまでにして、本題に戻る。
今回はこのclip-pathを使って、100を超える範囲についてのみ色が赤くなる棒グラフを作成する。
![スクリーンショット 2020-01-07 15.00.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547300/26802cbb-e111-d5ca-adda-6f92725a51c1.png)


作成の手順としては、まず切り取られる対象（棒グラフ）を作成する際、ほぼ全く同じ値を持ち、同じ動きをするグラフを2つ作る。そしてそのそれぞれに異なるclip-path属性を指定して、違う色を指定する。

```javascript

d3.select(graphSelector)
            .selectAll(".bar-below")
            .data(Data)
            .enter()
            .append("rect")
            .attr("clip-path", "url(#clip-below)")　//clip-path属性の指定
            .attr("class", "bar-below")
            .attr("x", d => Number(scale.x(d.x.toString())) + 5)
            .attr("y",  height)
            .attr("width", width / Data.length - barPadding)
            .attr("y", d => scale.y(d.y))
            .attr("height", d => height - scale.y(d.y))
            .style("fill", "blue"); //clip-belowで切り取られた範囲は青

        d3.select(graphSelector)
            .selectAll(".bar")
            .data(Data)
            .enter()
            .append("rect")
            .attr("x", d => Number(scale.x(d.x.toString())) + 5)
            .attr("y",  height)
            .attr("width", width / Data.length - barPadding)
            .attr("clip-path", "url(#clip-above)")　//clip-path属性の指定
            .attr("class", "bar-above")
            .attr("y", d => scale.y(d.y))
            .attr("height", d => height - scale.y(d.y))
            .style("fill", "red");　//clip-aboveで切り取られた範囲は赤
```
そして次に、切り取る範囲の指定を行う。ここでclipPathタグをappendしてそのタグにid="clip-above"を指定する。ここで指定された範囲は赤色でfillされる。
ここでは、y="0"、すなわち上端から、横幅はグラフコンポーネントの幅、縦幅はグラフコンポーネントの縦幅の上端から10/13まで、すなわち130~100までの範囲を切り取っている。
```
        d3.select(graphSelector)
            .data(Data)
            .append("clipPath")
            .attr("id", "clip-above")
            .append("rect")
            .attr("x", d => Number(scale.x(d.x.toString())) + 5)
            .attr("y", 0)
            .attr("width", width)
            //130から100までの高さを指定
            .attr("height", (height * 3) / 13)
```
同様にして、100以下の範囲も切り取る。切り取られた範囲と、グラフが重なる部分は青く描画される。

```javascript

        d3.select(graphSelector)
            .data(Data)
            .append("clipPath")
            .attr("id", "clip-below")
            .append("rect")
            .attr("x", d => Number(scale.x(d.x.toString())) + 5)
            //100%の高さを起点に指定
            .attr("y", (height * 3) / 13)
            .attr("width", width)
            .attr("height", 500)
```
ちなみにブラウザのHTMLはこんな感じ。作成の際の参考にしてほしい。

```html
<g class="graphContainer">
    <clipPath id="clip-above">
        <rect x="5" y="0" width="500" height="69.23076923076923"></rect>
    </clipPath>
    <clipPath id="clip-below">
        <rect x="5" y="69.23076923076923" width="500" height="500"></rect>
    </clipPath>
    <rect clip-path="url(#clip-below)" class="bar-below" x="5" y="184.6153846153846" width="45" height="115.38461538461539" style="fill: blue;"></rect>
    <rect clip-path="url(#clip-below)" class="bar-below" x="55" y="46.15384615384615" width="45" height="253.84615384615384" style="fill: blue;"></rect>
    <rect clip-path="url(#clip-below)" class="bar-below" x="105" y="230.76923076923075" width="45" height="69.23076923076925" style="fill: blue;"></rect>
    <rect clip-path="url(#clip-below)" class="bar-below" x="155" y="161.53846153846152" width="45" height="138.46153846153848" style="fill: blue;"></rect>
    <rect clip-path="url(#clip-below)" class="bar-below" x="205" y="23.076923076923062" width="45" height="276.92307692307696" style="fill: blue;"></rect>
    <rect clip-path="url(#clip-below)" class="bar-below" x="255" y="53.07692307692309" width="45" height="246.9230769230769" style="fill: blue;"></rect>
    <rect clip-path="url(#clip-below)" class="bar-below" x="305" y="76.15384615384615" width="45" height="223.84615384615387" style="fill: blue;"></rect>
    <rect clip-path="url(#clip-below)" class="bar-below" x="355" y="235.3846153846154" width="45" height="64.61538461538461" style="fill: blue;"></rect>
    <rect clip-path="url(#clip-below)" class="bar-below" x="405" y="140.76923076923077" width="45" height="159.23076923076923" style="fill: blue;"></rect>
    <rect clip-path="url(#clip-below)" class="bar-below" x="455" y="11.538461538461531" width="45" height="288.46153846153845" style="fill: blue;"></rect>
    <rect x="5" y="184.6153846153846" width="45" clip-path="url(#clip-above)" class="bar-above" height="115.38461538461539" style="fill: red;"></rect>
    <rect x="55" y="46.15384615384615" width="45" clip-path="url(#clip-above)" class="bar-above" height="253.84615384615384" style="fill: red;"></rect>
    <rect x="105" y="230.76923076923075" width="45" clip-path="url(#clip-above)" class="bar-above" height="69.23076923076925" style="fill: red;"></rect>
    <rect x="155" y="161.53846153846152" width="45" clip-path="url(#clip-above)" class="bar-above" height="138.46153846153848" style="fill: red;"></rect>
    <rect x="205" y="23.076923076923062" width="45" clip-path="url(#clip-above)" class="bar-above" height="276.92307692307696" style="fill: red;"></rect>
    <rect x="255" y="53.07692307692309" width="45" clip-path="url(#clip-above)" class="bar-above" height="246.9230769230769" style="fill: red;"></rect>
    <rect x="305" y="76.15384615384615" width="45" clip-path="url(#clip-above)" class="bar-above" height="223.84615384615387" style="fill: red;"></rect>
    <rect x="355" y="235.3846153846154" width="45" clip-path="url(#clip-above)" class="bar-above" height="64.61538461538461" style="fill: red;"></rect>
    <rect x="405" y="140.76923076923077" width="45" clip-path="url(#clip-above)" class="bar-above" height="159.23076923076923" style="fill: red;"></rect>
    <rect x="455" y="11.538461538461531" width="45" clip-path="url(#clip-above)" class="bar-above" height="288.46153846153845" style="fill: red;"></rect>
</g>
```
### 終わりに
clip-pathを使えば結構自由にグラフをアレンジできる。色を変える以外にも、使える用途がたくさんありそう。