余韻のある下線のあるリスト

ホバー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();
  });
});
Copyright Web Motion Catalog all rights reserved.