✏️

SVG形式アイコンをコンポーネント化した

2022/02/16に公開

はじめに

sweeep株式会社でエンジニアをしている、いまふくです。
新サービス「sweeep Box」では、Phosphor Icons からSVGをコピーし、アイコンとして使用しています。SVGタグをそのまま使用すると扱いづらい部分があったため、アイコンのコンポーネント化を行いました。

SVGとは

SVG(Scalable Vector Graphics)は画像フォーマットの一種です。
html, cssなどと同じく「タグ」で扱うことができます。

メリット

  • 拡張性・汎用性が高い
    後から色・サイズ・文字の変更が簡単
    アニメーションをつけることも可能
  • 拡大縮小による画像の劣化がない
    画像や文字などの2次元情報を数値化して記録していて、ブラウザがその場で描画する
  • ファイルサイズが軽い(特にアイコンなどシンプルなもの)

デメリット

  • コードを見ただけでは何を描画しているかがわからない
  • 写真など複雑な配色や輪郭の画像描写は、pngやjpgよりファイルサイズが大きくなる

コンポーネント化した

「コードを見ただけでは何を描画しているかがわからない」デメリットの解消、さらにサイズや色などをより簡単に指定・変更できるように、コンポーネント化を行いました。
編集可能な SVG アイコンシステム — Vue.js を参考に実装しています。

環境

@nuxt/cli v2.15.8

手順

  1. 使用するアイコンのSVGファイルを保存する
    regularとfilledを1つのファイルに保存し、propsの値により表示内容を出し分けます。
    可変にしたい項目(strokeやstroke-widthなど)は削除しています。

    components/svg/Eye.vue
    <template>
     <g v-if="filled">
       <path
         d="M247.3,124.8c-.3-.8-8.8-19.6-27.6-38.5C194.6,61.3,162.9,48,128,48S61.4,61.3,36.3,86.3C17.5,105.2,9,124,8.7,124.8a7.9,7.9,0,0,0,0,6.4c.3.8,8.8,19.6,27.6,38.5C61.4,194.7,93.1,208,128,208s66.6-13.3,91.7-38.3c18.8-18.9,27.3-37.7,27.6-38.5A7.9,7.9,0,0,0,247.3,124.8ZM128,92a36,36,0,1,1-36,36A36,36,0,0,1,128,92Z"
       ></path>
     </g>
     <g v-else>
       <path
         d="M128,56C48,56,16,128,16,128s32,72,112,72,112-72,112-72S208,56,128,56Z"
         fill="none"
         stroke-linecap="round"
         stroke-linejoin="round"
       ></path
       ><circle
         cx="128"
         cy="128"
         r="40"
         fill="none"
         stroke-linecap="round"
         stroke-linejoin="round"
       ></circle>
     </g>
    </template>
    
    <script>
    import { defineComponent } from '@vue/composition-api'
    
    export default defineComponent({
     props: {
       filled: {
         type: Boolean,
         default: false,
       },
     },
    })
    </script>
    
  2. スロットを使うベースアイコン(IconBase.vue)コンポーネントを作成する

    IconBase.vue
    <template>
      <svg
        xmlns="http://www.w3.org/2000/svg"
        :width="size"
        :height="size"
        :viewBox="viewBox"
        :class="addClass"
        :stroke="color"
        :stroke-width="filled ? '0' : weight"
        :fill="filled ? color : ''"
      >
        <slot />
      </svg>
    </template>
    
    <script>
    import { defineComponent } from '@vue/composition-api'
    
    export default defineComponent({
      props: {
        size: {
          type: Number,
          default: 20,
        },
        color: {
          type: String,
          default: '#000000',
        },
        weight: {
          type: Number,
          default: 16,
        },
        filled: {
          type: Boolean,
          default: false,
        },
        addClass: {
          type: String,
          default: '',
        },
        viewBox: {
          type: String,
          default: '0 0 256 256',
        },
      },
    })
    </script>
    
  3. 実際に使用する

    parent.vue
    <icon-base
      :size="12"
      color="red"
      class="animate-spin"
    >
      <eye />
    </icon-base>
    
    

おわりに

SVG形式ファイルをより分かりやすく扱う方法をご紹介しました。

最後に宣伝になりますが、弊社ではエンジニアを募集しています。ご興味のある方は下記リンクよりご応募お待ちしております!
https://www.wantedly.com/companies/sweeep/projects

Discussion