余韻のある下線のあるリスト
ホバーJavaScriptリスト
- List Item
- List Item
- List Item
- List Item
- List Item
- List Item
ホバーアウト時に余韻のある下線のアニメーションです。似たような動きはCSSだけでもできますが、すばやくマウスで通過したときは即座にアニメーションが終わり、あまり動きを感じられません。この実装ではホバーアウト時は100%の長さから始まるので通過時にもアニメーションが感じられます。
実装のポイント
ホバーイン、ホバーアウト時でそれぞれ別々のアニメーションをしたいのでWeb Animation APIを利用してJavaScriptで実装しています。ホバーアウト時のduration
を長くして余韻を持たせているのもポイントです。fill: "forwards"
にしないとアニメーションが終わったら下に戻ってしまうので、アニメーション終了時の状態を維持しています。
下線それぞれのアニメーションをキーフレームで定義したのち、リストアイテムにイベントリスナーを設置します。配列にリスト順で格納したアニメーションをインデックスで呼び出すことでアニメーションを実行しています。
コード例
HTML
<ul class="sampleList">
<li class="sampleItem">List Item <span class="underline"></span></li>
<li class="sampleItem">List Item <span class="underline"></span></li>
<li class="sampleItem">List Item <span class="underline"></span></li>
<li class="sampleItem">List Item <span class="underline"></span></li>
<li class="sampleItem">List Item <span class="underline"></span></li>
<li class="sampleItem">List Item <span class="underline"></span></li>
</ul>
CSS
.sampleItem {
position: relative;
line-height: 1.8;
/* 位置や太さは適宜設定してください */
.underline {
position: absolute;
left: 0;
bottom: 15%;
display: inline-block;
width: 0;
height: 2px;
background-color: currentColor;
}
}
JavaScript
// リストアイテムの要素を取得
const items = document.querySelectorAll(".sampleItem");
// 下線の要素を取得
const underlines = document.querySelectorAll(".underline");
// イージング関数
const easeOutQuart = "cubic-bezier(0.25, 1, 0.5, 1)";
// 下線のキーフレームアニメーション
const enterAnimationKeyframes = [
{ width: 0, easing: easeOutQuart },
{ width: "100%" },
];
const leaveAnimationKeyframes = [
{ width: "100%", easing: easeOutQuart },
{ width: 0 },
];
// それぞれの要素からアニメーションを作成し配列として保持しておく。インデックスはitemsと同じ
const underlineAnimations = Array.from(underlines).map((underline) => {
// 入るアニメーション
const enterAnimation = underline.animate(enterAnimationKeyframes, {
duration: 500,
iterations: 1,
fill: "forwards",
});
// 出るアニメーション
const leaveAnimation = underline.animate(leaveAnimationKeyframes, {
duration: 750,
iterations: 1,
fill: "forwards",
});
// 入るアニメーションはキャンセル(初期状態)で、出るアニメーションは終了(最終状態)しておくことで読み込み時のアニメーションを防ぐ
enterAnimation.cancel();
leaveAnimation.finish();
return { enterAnimation, leaveAnimation };
});
// アイテムそれぞれにイベントを設定
items.forEach((item, index) => {
item.addEventListener("mouseenter", () => {
underlineAnimations[index].leaveAnimation.cancel();
underlineAnimations[index].enterAnimation.play();
});
item.addEventListener("mouseleave", () => {
underlineAnimations[index].enterAnimation.cancel();
underlineAnimations[index].leaveAnimation.play();
});
});