Tailwind CSSでアイコンのアニメーション
はじめに
先日Tailwind CSSを開発しているAdam WathanさんがTwitterでSVGアイコンのアニメーションに関する以下のようなツイートをしていました。
Tailwind CSSを使って実装する手順が示されているのですが、面白そうだったので自分でもいくつか作成してみました。
作例①:伸縮させてみる
最初に、以下のようなMenu用のSVGアイコンを作成しました。
Tailwind Playでデモを作成しましたので、こちらからコードとプレビューが確認できます。
ボタン部分を作成する
まずはじめに、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のスタイルを構築していきます。各ユーティリティクラスの詳細等はドキュメントを参照ください。
アイコンを作成する
次に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-125
とgroup-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
クラスで位置の微調整をしています。
作例②:回してみる
次に、以下のようなUpdate用のSVGアイコンを作成しました。
基本の手順は作例①と同じとなります。今回は以下のように上下の矢印でそれぞれ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-linecap
、stroke-linejoin
属性はFigmaから書き出されたものを使用しています。
スタイリング部分は、先程と同様にgroup-hover:*
を使用して、rotate-45
クラスで回転量をorigin-center
クラスで回転の基準位置を指定しています。また、transition
クラスとduration-300
で300ミリ秒のアニメーションとしました。
それぞれのユーティリティの詳細は以下ドキュメントを参照ください。
作例③:バウンドしてみる
最後に、以下のようなDownload用のSVGアイコンを作成しました。
基本の手順は今までと同じです。今回は矢印と下側の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
内に追加したいクラスのスタイルを記述します。
/** @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',
},
},
},
...
}
keyframes
にbounce-little
クラスとanimation
にbounce-little
クラスを追加しています。これで、animate-bounce-little
クラスで独自のアニメーションが利用できるようになります。
まとめ
Tailwind CSSを利用してSVGの<line>
要素や<path>
要素などにclassを追加することで、比較的簡単にアイコンに動きをつけることができました。今回は試しに3つ程作ってみたのですが、本当に必要なアニメーションなのかどうかは考えなくてはいけないなと感じました。例えば3つ目のDownloadアイコンはホバー時のアニメーションとして実装していますが、ダウンロード中に見えてしまうかもしれません。
サンプルで作成したコードは以下のリポジトリとなります。何かの参考となれば幸いです。
ちなみにAdam Wathanさんのツイートの最後にもありますが、バケーション中にこの実装を考えていたようで、なんだか素敵でした。
Discussion