WebでもiOSのアイコンみたいな角丸を再現する

## これは何

- iOSのアイコンがただの角丸ではなく、曲率変化が滑らかな角丸であるのは有名だと思います
    - 古い記事ですがこちらに詳しく書かれています
    - https://hackernoon.com/apples-icons-have-that-shape-for-a-very-good-reason-720d4e7c8a14
- それをWeb上でもどうにか再現しようと試みて、それなりに上手くいったのでまとめてみました
    - 最終形は以下のgifのようになります

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/5719fa4c-dcd4-e5f9-2f3c-6360e9d05e59.gif)

## 滑らかな角丸四角形をSVGで再現する

CSSだけではどう頑張っても滑らかな角丸は作れないので、SVGを作成して切り貼りする方向で考えました。
まずはFigmaで形を作ります。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/aefdb383-cd74-af97-5ac6-d3c4fe3dba02.png)

左側のような角丸四角形を作るつもりでいきましょう。

余談ですが、Figmaで滑らかな角丸四角形を作るやり方はこちらの記事で解説しています。

https://qiita.com/xrxoxcxox/items/cd27f7384793d2896f47#%E8%A7%92%E4%B8%B8%E3%81%AE%E4%B8%B8%E3%81%BE%E3%82%8A%E6%96%B9%E3%82%92%E6%BB%91%E3%82%89%E3%81%8B%E3%81%AB%E6%8C%87%E5%AE%9A%E3%81%A7%E3%81%8D%E3%82%8B%E3%82%B9%E3%83%BC%E3%83%91%E3%83%BC%E6%A5%95%E5%86%86%E3%81%A3%E3%81%BD%E3%81%84%E3%82%82%E3%81%AE%E3%81%8C%E4%BD%9C%E3%82%8C%E3%82%8B

## 1つの角をシンボルとして登録する

書き出したSVGをそのまま貼り付けては、角丸が上手く拡大縮小しません。
例えば横幅を伸ばすとこんな感じになっていまいます。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/febce6a2-8d5e-9fd7-010c-da4723bc00f6.png)

これを解消するために、角丸とそれ以外の部分を分けて実装しましょう。
角丸部分は常に同じサイズで、直線部分だけが拡大縮小されるようにすれば問題ありません。

HTML、CSSにそれぞれ以下のようにコードを書きます。

```html
<!-- DOCTYPE宣言などは省略 -->
<body>
  <div class="wrapper">
    <svg
      xmlns="http://www.w3.org/2000/svg"
      class="svgRoot"
    >
      <style type="text/css">
        .svgRoot {
          position: relative;
        }
      </style>
      <defs>
        <symbol
          id="corner"
          width="100px"
          height="100px"
          viewBox="0 0 100 100"
        >
          <path d="M0 100C0 52.8595 0 29.2893 14.6447 14.6447C29.2893 0 52.8595 0 100 0V0V100H0V100Z" />
        </symbol>
      </defs>

      <!-- top left corner -->
      <use xlink:href="#corner" width="100px" height="100px" x="0" y="0" />
    </svg>
  </div>
</body>
```

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

html, body {
  height: 100%;
}

.wrapper {
  height: 100%;
  padding: 200px;
}

.svgRoot {
  display: block;
  width: 100%;
  height: 100%;
  fill: #55c500;
}
```

現時点ではこのような見た目になっているはずです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/ae8669fb-f4ff-7e44-45a3-f8f3eb372444.png)

### defs, symbol, useとは

SVGでは実は要素の再利用が可能です。
`defs`の中で`symbol`として定義し、後から`use`でインスタンス化するようなイメージだと分かりやすいのではないでしょうか。

`defs`や`symbol`を使わず画面内に描画されている要素を直接`use`で呼び出すことも可能なのですが、推奨はされていないようです。

https://developer.mozilla.org/ja/docs/Web/SVG/Element/defs

## `use`を用いて4つの角を作る

コードとしては以下のようになります。

```diff_html
    <div class="wrapper">
      <svg
        xmlns="http://www.w3.org/2000/svg"
        class="svgRoot"
      >
        <style type="text/css">
          .svgRoot {
            position: relative;
          }
        </style>
        <defs>
          <symbol
            id="corner"
            width="100px"
            height="100px"
            viewBox="0 0 100 100"
          >
            <path d="M0 100C0 52.8595 0 29.2893 14.6447 14.6447C29.2893 0 52.8595 0 100 0V0V100H0V100Z" />
          </symbol>
        </defs>

        <!-- top left corner -->
        <use xlink:href="#corner" width="100px" height="100px" x="0" y="0" />
+
+       <!-- top right corner -->
+       <use
+         xlink:href="#corner"
+         transform="scale(-1, 1)"
+         width="100px"
+         height="100px"
+         x="-100%"
+         y="0"
+       />
+
+       <!-- bottom left corner -->
+       <use
+         xlink:href="#corner"
+         transform="scale(1, -1)"
+         width="100px"
+         height="100px"
+         x="0"
+         y="-100%"
+       />
+
+       <!-- bottom right corner -->
+       <use
+         xlink:href="#corner"
+         transform="scale(-1, -1)"
+         width="100px"
+         height="100px"
+         x="-100%"
+         y="-100%"
+       />
      </svg>
    </div>
```

見た目はこんな感じ。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/ce6f6a83-938f-76a9-a2d4-1e6137da0aec.gif)

## 角以外の部分を埋める

ここまで来たら後は簡単です。
間の部分を塗りで埋めれば良いだけ。

```diff_html
    <div class="wrapper">
      <svg
        xmlns="http://www.w3.org/2000/svg"
        class="svgRoot"
      >
        <style type="text/css">
          .svgRoot {
            position: relative;
          }
+         .edge {
+           height: 100px;
+           width: calc(100% - 200px);
+         }
+         .center {
+           height: calc(100% - 200px);
+           width: 100%;
+         }
        </style>
        <defs>
          <symbol
            id="corner"
            width="100px"
            height="100px"
            viewBox="0 0 100 100"
          >
            <path d="M0 100C0 52.8595 0 29.2893 14.6447 14.6447C29.2893 0 52.8595 0 100 0V0V100H0V100Z" />
          </symbol>
        </defs>

        <!-- top left corner -->
        <use xlink:href="#corner" width="100px" height="100px" x="0" y="0" />

+       <!-- top edge -->
+       <rect x="100" y="0" width="100%" height="100%" class="edge" />

        <!-- top right corner -->
        <use
          xlink:href="#corner"
          transform="scale(-1, 1)"
          width="100px"
          height="100px"
          x="-100%"
          y="0"
        />

+       <!-- center -->
+       <rect x="0" y="100" width="100%" height="100%" class="center" />

        <!-- bottom left corner -->
        <use
          xlink:href="#corner"
          transform="scale(1, -1)"
          width="100px"
          height="100px"
          x="0"
          y="-100%"
        />

+       <!-- bottom edge -->
+       <rect transform="scale(1, -1)" x="100" y="-100%" width="100%" height="100%" class="edge" />

        <!-- bottom right corner -->
        <use
          xlink:href="#corner"
          transform="scale(-1, -1)"
          width="100px"
          height="100px"
          x="-100%"
          y="-100%"
        />
      </svg>
    </div>
```

これで、冒頭に貼ったような拡大縮小可能な、滑らかな角丸の四角形が完成しました。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/5719fa4c-dcd4-e5f9-2f3c-6360e9d05e59.gif)

## 参考にしたコード

https://codepen.io/DirkWeber/pen/mDiIs