🕊

Tailwind CSSでアイコンのアニメーション

2022/09/27に公開

はじめに

先日Tailwind CSSを開発しているAdam WathanさんがTwitterでSVGアイコンのアニメーションに関する以下のようなツイートをしていました。

https://twitter.com/adamwathan/status/1571175551393730560?s=20&t=5UvbLW4_tmiOVNnfMksVXA

Tailwind CSSを使って実装する手順が示されているのですが、面白そうだったので自分でもいくつか作成してみました。

作例①:伸縮させてみる

最初に、以下のようなMenu用のSVGアイコンを作成しました。

Tailwind Playでデモを作成しましたので、こちらからコードとプレビューが確認できます。
https://play.tailwindcss.com/wEuM5fPcZF

ボタン部分を作成する

まずはじめに、Tailwind CSSのユーティリティクラスを使用してボタンをスタイリングします。

<button
  type="button"
  class="rounded-full bg-gray-700 px-4 py-2 font-bold text-white hover:bg-gray-900"
>
  MENU
</button>

これで以下のようなボタン型の見た目となります。

rounded-fullで角丸に、bg-gray-700で背景色をグレーに、といった具合にTailwind CSS では、基本的にこのようなユーティリティクラスを組み合わせてUIのスタイルを構築していきます。各ユーティリティクラスの詳細等はドキュメントを参照ください。
https://tailwindcss.com/

アイコンを作成する

次にSVGアイコン部分を作成します。デザインツールで完成形のアイコンを作り、それをアニメーションさせたいパスごとに分割し、それぞれをSVGで書き出します。

svg要素をスタイリングする

書き出したSVGは以下のようになっています。

<svg
  xmlns="http://www.w3.org/2000/svg"
  fill="none"
  width="24"
  height="24"
  viewBox="0 0 24 24"
>
  <line x1="2" y1="5" x2="22" y2="5" stroke="black" stroke-width="2" />
</svg>

これをそれぞれのパス分作成し、<line>要素を抽出して同じviewBoxを持つ1つのSVGにまとめます。stroke属性とstroke-widht属性は削除して、<svg>要素のclassで指定しています。

 <svg
+  class="stroke-gray-50 stroke-2"
   xmlns="http://www.w3.org/2000/svg"
   fill="none"
   width="24"
   height="24"
   viewBox="0 0 24 24"
 >
-  <line x1="2" y1="5" x2="22" y2="5" stroke="black" stroke-width="2"/>
+  <line x1="2" y1="5" x2="22" y2="5" />
+  <line x1="2" y1="12" x2="18" y2="12" />
+  <line x1="2" y1="19" x2="22" y2="19" />
 </svg>

そしてアニメーションさせたい<line>要素に対して、アニメーション用のclassを記述します。

 <button
   type="button"
+  class="group inline-flex items-center gap-2 rounded-full bg-gray-700 px-4 py-1.5 font-semibold text-white hover:bg-gray-900"
 >
   MENU
   <svg ...>
     <line x1="2" y1="5" x2="22" y2="5" />
-    <line x1="2" y1="12" x2="18" y2="12" />
+    <line x1="2" y1="12" x2="18" y2="12"
+      class="transition group-hover:translate-x-[-0.5px] group-hover:scale-x-125" />
-    <line x1="2" y1="19" x2="22" y2="19" />
+    <line x1="2" y1="19" x2="22" y2="19"
+       class="transition group-hover:translate-x-[1px] group-hover:scale-x-50" />
   </svg>
 </button>

group-hover:scale-x-125group-hover:scale-x-50の部分ですが、まずscale-xクラスでx方向に要素の伸縮をしています。伸ばす要素にscale-x-125、縮める要素にscale-x-50としました。クラスの前にhover:*等と記述することで擬似クラスが表現できます。また、groupバリアントを使用すると、親要素の状態に基づいて要素にスタイル付けすることができます。親の<button>要素にgroupクラスを付与し、子孫要素の<line>group-hover:*と記述すると<button>要素にhoverした際にも、<line>要素にスタイルが適用されます。

まとめると、親要素にgroupクラス、<line>要素にgroup-hover:scale-*クラスを記述することで親要素にhoverした際にも<line>要素が伸縮する、ということになります。

あとはtransitionクラスでアニメーションを、translateクラスで位置の微調整をしています。

https://tailwindcss.com/docs/hover-focus-and-other-states

作例②:回してみる

次に、以下のようなUpdate用のSVGアイコンを作成しました。


https://play.tailwindcss.com/Hy2AllB99G

基本の手順は作例①と同じとなります。今回は以下のように上下の矢印でそれぞれpathを分割しました。

実装した<svg>要素部分は以下のようになっています。

<svg class="stroke-gray-50 stroke-2" ...>
  <path
    d="M17 9.3466L21.0154 9.34838V9.3466M4.03076 9.86481C5.21003 5.46371 9.73381 2.85191 14.1349 4.03118C15.5867 4.42017 16.8437 5.17309 17.8343 6.16547L21.0154 9.3466M21.0154 5.5V9.3466"
    stroke-linecap="round"
    stroke-linejoin="round"
    class="origin-center transition duration-300 group-hover:rotate-45"
  />

  <path
    d="M2.98413 18.5V14.6517M2.98413 14.6517H7M2.98413 14.6517L6.16502 17.8347C7.15555 18.827 8.41261 19.58 9.86436 19.9689C14.2654 21.1482 18.7892 18.5364 19.9685 14.1353"
    stroke-linecap="round"
    stroke-linejoin="round"
    class="origin-center transition duration-300 group-hover:rotate-45"
  />
</svg>

<path>要素のd属性とstroke-linecapstroke-linejoin属性はFigmaから書き出されたものを使用しています。

スタイリング部分は、先程と同様にgroup-hover:*を使用して、rotate-45クラスで回転量をorigin-centerクラスで回転の基準位置を指定しています。また、transitionクラスとduration-300で300ミリ秒のアニメーションとしました。

それぞれのユーティリティの詳細は以下ドキュメントを参照ください。

作例③:バウンドしてみる

最後に、以下のようなDownload用のSVGアイコンを作成しました。


https://play.tailwindcss.com/az28vTHGDb?file=config

基本の手順は今までと同じです。今回は矢印と下側のpathで分割しました。

実装した<svg>要素部分は以下のようになっています。

<svg class="stroke-gray-50 stroke-2" ...>
  <path
    d="M16.5 12L12 16.5M12 16.5L7.5 12M12 16.5V6"
    stroke-linecap="round"
    stroke-linejoin="round"
    class="group-hover:animate-bounce-little origin-center transition"
  />
  >
  <path
    d="M3 16.5V18.75C3 19.9926 4.00736 21 5.25 21H18.75C19.9926 21 21 19.9926 21 18.75V16.5"
    stroke-linecap="round"
    stroke-linejoin="round"
  />
</svg>

矢印部分のpathのアニメーションをanimate-bounce-littleの独自クラスで実装しています。独自のユーティリティクラスはTailwind CSSのconfigファイルで設定することができます。themeのデフォルト値を維持しつつ新しい値を追加したい場合は、themeセクションのextend内に追加したいクラスのスタイルを記述します。

tailwind.config.cjs
/** @type {import('tailwindcss').Config} */
module.exports = {
  theme: {
    extend: {
      keyframes: {
        'bounce-little': {
          '0%, 100%': {
            transform: 'translateY(-10%)',
            'animation-timing-function': 'cubic-bezier(0.8, 0, 1, 1)',
          },
          '50%': {
            transform: 'translateY(0)',
            'animation-timing-function': 'cubic-bezier(0, 0, 0.2, 1)',
          },
        },
      },
      animation: {
        'bounce-little': 'bounce-little 0.7s linear infinite',
      },
    },
  },
  ...
}

keyframesbounce-littleクラスとanimationbounce-littleクラスを追加しています。これで、animate-bounce-littleクラスで独自のアニメーションが利用できるようになります。

https://developer.mozilla.org/en-US/docs/Web/CSS/@keyframes
https://developer.mozilla.org/en-US/docs/Web/CSS/animation

まとめ

Tailwind CSSを利用してSVGの<line>要素や<path>要素などにclassを追加することで、比較的簡単にアイコンに動きをつけることができました。今回は試しに3つ程作ってみたのですが、本当に必要なアニメーションなのかどうかは考えなくてはいけないなと感じました。例えば3つ目のDownloadアイコンはホバー時のアニメーションとして実装していますが、ダウンロード中に見えてしまうかもしれません。

サンプルで作成したコードは以下のリポジトリとなります。何かの参考となれば幸いです。
https://github.com/K-shigehito/svg-icon-animation

ちなみにAdam Wathanさんのツイートの最後にもありますが、バケーション中にこの実装を考えていたようで、なんだか素敵でした。

参考

GitHubで編集を提案

Discussion