svg のリファクタリングの流れ

## この記事の概要

ある日、こんなアイコンを作っていました。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/6372f2d1-3150-f2c4-3152-bbcfe1785db6.png" alt="" width="240">

svg で書き出しをしたら次のようなコードになりました。

```xml
<svg width="18" height="14" viewBox="0 0 18 14" fill="none" xmlns="http://www.w3.org/2000/svg">
  <path fill-rule="evenodd" clip-rule="evenodd" d="M1 0L0 1C2.99988 2 7 5 9.00003 7C11 5 15 2 18 1L17 0C12.9999 1.00006 9 4 9 4C9 4 4.99988 1.00005 1 0ZM1 7.00006L0 8.00006C2.99988 9.00006 7 12.0001 9.00003 14.0001C11 12.0001 15 9.00006 18 8.00006L17 7.00006C12.9999 8.00012 9 11.0001 9 11.0001C9 11.0001 4.99988 8.00011 1 7.00006Z" fill="#000"/>
</svg>
```

パッと見て「こんなに長々としたコードじゃなくても絶対大丈夫でしょう」と思ったので次のようにリファクタリングをしました。

```xml
<svg width="18" height="14" viewBox="0 0 18 14" fill="#000" xmlns="http://www.w3.org/2000/svg" >
  <path d="M1 0L0 1C3 2 7 5 9 7C11 5 15 2 18 1L17 0C13 1 9 4 9 4C9 4 5 1 1 0Z" />
  <path d="M1 7L0 8C3 9 7 12 9 14C11 12 15 9 18 8L17 7C13 8 9 11 9 11C9 11 5 8 1 7Z"/>
</svg>
```

この通り、見た目も変わっていません。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/901c97af-e4af-cd88-86b6-6a76eac32263.png" alt="" width="240">

このとき、リファクタリングをしていた自分の頭の中を備忘録がてら記事にします。

## 人間に分かりやすい書式に直す

実際はこの工程は踏んでいませんが、説明をしやすくするために記事では実施します。
svg のコードは `M` とか `L` とかの文字と、その後のいくつかの数字の組み合わせで成り立っています。[^svg-format]
そのため、ルールを見やすくするための整形をします。

[^svg-format]: MDN Docs が分かりやすいと思います。例えば [`d` 属性においてはこのドキュメント](https://developer.mozilla.org/ja/docs/Web/SVG/Attribute/d) です。

```diff_xml
  <svg width="18" height="14" viewBox="0 0 18 14" fill="none" xmlns="http://www.w3.org/2000/svg">
-   <path fill-rule="evenodd" clip-rule="evenodd" d="M1 0L0 1C2.99988 2 7 5 9.00003 7C11 5 15 2 18 1L17 0C12.9999 1.00006 9 4 9 4C9 4 4.99988 1.00005 1 0ZM1 7.00006L0 8.00006C2.99988 9.00006 7 12.0001 9.00003 14.0001C11 12.0001 15 9.00006 18 8.00006L17 7.00006C12.9999 8.00012 9 11.0001 9 11.0001C9 11.0001 4.99988 8.00011 1 7.00006Z" fill="#000"/>
+   <path
+     fill-rule="evenodd"
+     clip-rule="evenodd"
+     d="M 1 0
+        L 0 1
+        C 2.99988 2 7 5 9.00003 7
+        C 11 5 15 2 18 1
+        L 17 0
+        C 12.9999 1.00006 9 4 9 4
+        C 9 4 4.99988 1.00005 1 0
+        Z
+        M 1 7.00006
+        L 0 8.00006
+        C 2.99988 9.00006 7 12.0001 9.00003 14.0001
+        C 11 12.0001 15 9.00006 18 8.00006
+        L 17 7.00006
+        C 12.9999 8.00012 9 11.0001 9 11.0001
+        C 9 11.0001 4.99988 8.00011 1 7.00006
+        Z"
+      fill="#000"
+   />
  </svg>
```

## `fill-rule` と `clip-rule` を削除する

`fill-rule` と `clip-rule` は、ざっくり言えば `抜き` が必要な場合に使うものです。
次のようなイメージです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/d19002b6-f366-690e-7366-7a127290b83d.png" alt="" width="240">

しかし今回作っていたアイコンは `抜き` が無いので不要です。
グラフィックソフトが気を利かせて（？）付与しているだけなので削除します。

```diff_xml
  <svg width="18" height="14" viewBox="0 0 18 14" fill="none" xmlns="http://www.w3.org/2000/svg">
    <path
-     fill-rule="evenodd"
-     clip-rule="evenodd"
      d="M 1 0
         L 0 1
         C 2.99988 2 7 5 9.00003 7
         C 11 5 15 2 18 1
         L 17 0
         C 12.9999 1.00006 9 4 9 4
         C 9 4 4.99988 1.00005 1 0
         Z
         M 1 7.00006
         L 0 8.00006
         C 2.99988 9.00006 7 12.0001 9.00003 14.0001
         C 11 12.0001 15 9.00006 18 8.00006
         L 17 7.00006
         C 12.9999 8.00012 9 11.0001 9 11.0001
         C 9 11.0001 4.99988 8.00011 1 7.00006
         Z"
       fill="#000"
    />
  </svg>
```

## やたら細かい小数点をなくす

元々のコードでは、小数第 5 位までが記載されています。
グラフィックソフト上ではピクセルにスナップさせながら作ったのですが、書き出し時の精度の問題か何かなのでしょう。
というわけでまずは整数にします。

```diff_xml
  <svg width="18" height="14" viewBox="0 0 18 14" fill="none" xmlns="http://www.w3.org/2000/svg">
    <path
      d="M 1 0
         L 0 1
-        C 2.99988 2 7 5 9.00003 7
+        C 3 2 7 5 9 7
         C 11 5 15 2 18 1
         L 17 0
-        C 12.9999 1.00006 9 4 9 4
-        C 9 4 4.99988 1.00005 1 0
+        C 13 1 9 4 9 4
+        C 9 4 5 1 1 0
         Z
-        M 1 7.00006
-        L 0 8.00006
-        C 2.99988 9.00006 7 12.0001 9.00003 14.0001
-        C 11 12.0001 15 9.00006 18 8.00006
-        L 17 7.00006
-        C 12.9999 8.00012 9 11.0001 9 11.0001
-        C 9 11.0001 4.99988 8.00011 1 7.00006
+        M 1 7
+        L 0 8
+        C 3 9 7 12 9 14
+        C 11 12 15 9 18 8
+        L 17 7
+        C 13 8 9 11 9 11
+        C 9 11 5 8 1 7
         Z"
       fill="#000"
    />
  </svg>
```

だいぶスッキリしました。

## `close-path` で `path` を分ける

`path` の中にあるアルファベットのうち、 `Z, z` は `close-path` を表します。
今回は 1 つの `path` の中に `Z` が 2 回登場しているので、分けてしまった方がシンプルに思えます（好みの問題もあるかもしれませんが……）。

```diff_xml
  <svg width="18" height="14" viewBox="0 0 18 14" fill="none" xmlns="http://www.w3.org/2000/svg">
    <path
      d="M 1 0
         L 0 1
         C 3 2 7 5 9 7
         C 11 5 15 2 18 1
         L 17 0
         C 13 1 9 4 9 4
         C 9 4 5 1 1 0
-        Z
-        M 1 7
-        L 0 8
-        C 3 9 7 12 9 14
-        C 11 12 15 9 18 8
-        L 17 7
-        C 13 8 9 11 9 11
-        C 9 11 5 8 1 7
         Z"
       fill="#000"
    />
+   <path
+     d="M 1 7
+        L 0 8
+        C 3 9 7 12 9 14
+        C 11 12 15 9 18 8
+        L 17 7
+        C 13 8 9 11 9 11
+        C 9 11 5 8 1 7
+        Z"
+      fill="#000"
+   />
  </svg>
```

## `fill` を 1 箇所にまとめる

`path` がたくさんあってそれぞれに色をつけたいならいざ知らず、今回はそんなことはありません。
DRY[^dry] にします。

[^dry]: 今回の場合 `symbol` と `use` を使えばもうコードを少なくできます。ただ、 `意図して同じパスを 2 回使っている` というよりは `たまたま同じパスが 2 回使われていた`　という方が正しい気がするので DRY とはまた違うと考えました（視覚調整のために上下で微妙に変えることもありそうですしね）。というわけで `symbol` と `use` は使用していません。

```diff_xml
- <svg width="18" height="14" viewBox="0 0 18 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <svg width="18" height="14" viewBox="0 0 18 14" fill="#000" xmlns="http://www.w3.org/2000/svg">
    <path
      d="M 1 0
         L 0 1
         C 3 2 7 5 9 7
         C 11 5 15 2 18 1
         L 17 0
         C 13 1 9 4 9 4
         C 9 4 5 1 1 0
         Z"
-      fill="#000"
    />
    <path
      d="M 1 7
         L 0 8
         C 3 9 7 12 9 14
         C 11 12 15 9 18 8
         L 17 7
         C 13 8 9 11 9 11
         C 9 11 5 8 1 7
         Z"
-      fill="#000"
    />
  </svg>
```

## 完成

最終形です。

```xml
<svg width="18" height="14" viewBox="0 0 18 14" fill="#000" xmlns="http://www.w3.org/2000/svg">
  <path
    d="M 1 0
       L 0 1
       C 3 2 7 5 9 7
       C 11 5 15 2 18 1
       L 17 0
       C 13 1 9 4 9 4
       C 9 4 5 1 1 0
       Z"
  />
  <path
    d="M 1 7
       L 0 8
       C 3 9 7 12 9 14
       C 11 12 15 9 18 8
       L 17 7
       C 13 8 9 11 9 11
       C 9 11 5 8 1 7
       Z"
  />
</svg>
```

ただし実際は改行する意味も無いので、このようになっています（記事冒頭で示したのと同じコードです）。

```xml
<svg width="18" height="14" viewBox="0 0 18 14" fill="#000" xmlns="http://www.w3.org/2000/svg" >
  <path d="M1 0L0 1C3 2 7 5 9 7C11 5 15 2 18 1L17 0C13 1 9 4 9 4C9 4 5 1 1 0Z" />
  <path d="M1 7L0 8C3 9 7 12 9 14C11 12 15 9 18 8L17 7C13 8 9 11 9 11C9 11 5 8 1 7Z"/>
</svg>
```

## 最後に

svg 最適化において、見た目を勝手に変えないようになのかパスを弄ってくれるツールにはまだ出会えていません（出会えていないだけで、存在はするのかもしれませんが……）。

というわけで、自分で作ったアイコンであれば今回のようにコード削減をする場合もあります。
パフォーマンス改善に有効とも思えませんが、自分の好みで実施しています。

共感してくださる方がいたらぜひ、いいねやコメントお願いします！
なんせ、共感してもらえた試しがありません。

---

最後まで読んでくださってありがとうございます！
[Twitter](https://twitter.com/xrxoxcxox)でも情報を発信しているので、良かったらフォローお願いします！

[Devトーク](https://jobs.qiita.com/employers/qiita-inc/dev_talks/489)でお話してくださる方も募集中です！