🐈

要素の上にマウスが乗ったときにアイコンを出現させる実装

2023/07/03に公開

イントロ

現在、JavaScriptを用いたアニメーションの実装を勉強中です。

今回、特定の要素にカーソルが乗ったときにアイコンが出現する、カーソルにアイコンが追従する、実装を行ってみたので、公開したいと思います!

実装例

コーディング

HTML

<body>
  <section>
    <div class="section_inner">
      <ul class="list">
        <li class="item"></li>
        <li class="item"></li>
        <li class="item"></li>
        <li class="item"></li>
        <li class="item"></li>
        <li class="item"></li>
        <li class="item"></li>
        <li class="item"></li>
      </ul>
    </div>
  </section>
  <div class="pointer">
    <img src="icon-pointer.svg" width="34" height="12" alt="" />
  </div>
</body>

ボディ直下にアイコンを設置することで、ページ全体で使えるようにしました。

css

.pointer {
  position: fixed;
  z-index: 10;
  top: -48px;
  left: -48px;
  width: 96px;
  height: 96px;
  border-radius: 50%;
	pointer-events: none;
  opacity: 0;
  visibility: hidden;

  &.-isShown {
    opacity: 1;
    visibility: visible;
  }

  img {
    width: 100%;
    height: 100%;
  }
}

今回、JavaScriptでスタリングのtransformを制御して、出現したアイコンの移動を行っています。そのため、transform:translate(-50%, -50%)を設定すると、実装がややこしくなるため、それを避けるためにwidth,heightで位置調整を行っています。

また、アイコンのガタツキをなくすために、pointer-events: noneを設定しました。(これを設定しないと、出現の有無が不安定になる、、、)

JavaScript

class PointerIcon {
  items: NodeListOf<HTMLElement> = document.querySelectorAll('.item');
  pointer: HTMLDivElement | null = document.querySelector('.pointer');

  constructor() {
    this.items.forEach((item) => {
      item.addEventListener('mouseenter', (event) => this.showPointer(event));
      item.addEventListener('mousemove', (event) => this.movePointer(event));
      item.addEventListener('mouseleave', (event) => this.hidePointer(event));
    });
  }

  showPointer(event: any) {
    this.pointer?.classList.add('-isShown');
    this.movePointer(event);
  }

  movePointer(event: any) {
    const x = event.clientX;
    const y = event.clientY;
    this.pointer.style.transform = `translate(${x}px, ${y}px)`;
  }

  hidePointer(event: any) {
    this.pointer?.classList.remove('-isShown');
    this.movePointer(event);
  }
}

(() => {
  new PointerIcon();
})();

アイコンの出現は、イベントリスナーとクラスの取り外しで制御しています。

  • 要素の上にカーソルが乗っていないときは、アイコンは非表示状態。
  • 要素の上にカーソルが乗ったときは、イベントリスナー'mouseenter'を使って、classを付与して、アイコンを表示する。
  • 要素からカーソルが外れたときは、イベントリスナー'mouseleave'を使って、classを外して、アイコンを非表示にする。
opacity: 0;
visibility: hidden;

&.-isShown {
  opacity: 1;
  visibility: visible;
}

カーソルの移動に追従してアイコンが移動する実装は、

const x = event.clientX;
const y = event.clientY;
this.pointer.style.transform = `translate(${x}px, ${y}px)`;

部分で行っています。
clientXはビューポートにおけるカーソルの水平方向の位置を取得するプロパティ。
clientYはビューポートにおけるカーソルの垂直方向の位置を取得するプロパティ。

イベントリスナー

  • mouseenterとmousemoveの違い。
    • mouseenterは要素の上に乗ったときに判定がtrueになる。mousemoveは、要素の上でカーソルが動く度にtrue判定される。

今回の学び

今回の実装で、学びになったことは、用途に応じてイベントリスナーを複数使うことです。最初、イベントリスナーひとつで、アイコンの出現を制御しようとしたため方法が思い浮かびませんでした。しかし、他の人の実装を調査すしていると、イベントリスナーを複数使う(開始、動作中、終了)のがデフォルトなことがわかりました(たぶん、当たり前の実装だとは思うのですが、、、)。今後の実装でも、このセットを使用していきたいと思います!

もうひとつが、pointer-events: none;です。アイコンがガタついてどうしたらいいのか分からなかったのですが、、pointer-events: none;を記述することでガタつきを回避することができました。

まとめ

JavaScriptを使ったアニメーションは、イベントリスナーやスタイリングなど理解できていない部分がまだまだあるため、随時学んでアウトプットしていきたいと思います!

Discussion