要素に入るとポインターに追従して白い丸が動く実装をしてみる(備忘録)

今回は、要素に入るとポインターに追従して白い丸が動く実装をしていきます。ポインター自体を別のものに切り替えるシリーズは一旦終わりです。

前回の参考記事はこちら

Webデザインギャラリーでよくみかけると思いましたので、こちらのいくつか取り上げていく予定です。

HTMLの記述について

<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>要素に入るとポインターに追従して白い丸が動く実装をしてみる(備忘録)</title>
  <link rel="stylesheet" href="../reset.css">
  <link rel="stylesheet" href="./style.css">
</head>

<body>
  <main>
    <div class="pointer-area"></div>
  </main>
  <script src="./style.js"></script>
</body>

</html>

これは前回と同じのため、解説は省略します。

CSSの記述について

@charset "utf-8";

/* ==========================
  初期設定
========================== */
*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  position: relative;
  word-wrap: break-word;
}

img {
  width: 100%;
  vertical-align: bottom;
}

/* レイアウト設定 */
main {
  margin: 80px 0;
}

.pointer-area {
  background-color: #c3512f;
  height: 400px;
  cursor: auto;
}

.custom-cursor {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background-color: #fff;
  position: fixed;
  pointer-events: none;
  display: none;
  z-index: 1000;
  transition: opacity 0.5s ease-out;
}

custom-cursorでポインターに追従する白い丸の設定をしています。色や形などは任意のものに変えることができます。。

JavaScriptの記述について

"use strict";

document.addEventListener("DOMContentLoaded", () => {
  // .pointer-area 要素を取得
  const pointerArea = document.querySelector(".pointer-area");

  // カスタムカーソル要素を作成
  const cursor = document.createElement("div");
  cursor.classList.add("custom-cursor");

  // カスタムカーソル要素をボディに追加
  document.body.appendChild(cursor);

  // マウスとカーソルの位置を保持する変数を初期化
  let mouseX = 0,
    mouseY = 0;
  let cursorX = 0,
    cursorY = 0;
  let fadeTimeout; // フェードアウトのタイマーを保持する変数

  // マウスが pointerArea に入った時の処理
  pointerArea.addEventListener("mouseenter", () => {
    // フェードアウトタイマーが存在する場合はクリア
    if (fadeTimeout) {
      clearTimeout(fadeTimeout);
    }
    cursor.style.display = "block"; // カスタムカーソルを表示
    cursor.style.opacity = 1; // カスタムカーソルの透明度を1に設定
  });

  // マウスが pointerArea 内で動いた時の処理
  pointerArea.addEventListener("mousemove", (e) => {
    // マウスの位置を取得
    mouseX = e.clientX;
    mouseY = e.clientY;

    // カーソルの初期位置が未設定の場合は設定
    if (!cursorX && !cursorY) {
      cursorX = mouseX;
      cursorY = mouseY;
    }
  });

  // マウスが pointerArea から出た時の処理
  pointerArea.addEventListener("mouseleave", () => {
    cursor.style.opacity = 0; // カスタムカーソルの透明度を0に設定(フェードアウト開始)
    fadeTimeout = setTimeout(() => {
      cursor.style.display = "none"; // フェードアウト後にカスタムカーソルを非表示に
    }, 500); // フェードアウトの時間に合わせてタイマーを設定
  });

  // カスタムカーソルをマウスに追従させるアニメーション関数
  function animate() {
    // カーソルの位置を徐々にマウスの位置に近づける
    cursorX += (mouseX - cursorX) * 0.1;
    cursorY += (mouseY - cursorY) * 0.1;

    // カスタムカーソルの位置を更新(白い丸の中心をポインタの中心に合わせるために補正)
    cursor.style.left = cursorX - 16 + "px";
    cursor.style.top = cursorY - 16 + "px";

    // アニメーションフレームをリクエスト
    requestAnimationFrame(animate);
  }

  // アニメーションを開始
  animate();
});

詳細はコメントアウトで記載しているとおりです。

animate関数のところで、白い丸がポインター(マウス)に追従するように設定しています。0.1の値を大きくするとより速くポインターに追従するようになります。その逆はゆっくりになります。

requestAnimationFrameは、アニメーションをスムーズに実行するために使われます。60FPS(1秒間に60フレーム)のアニメーションを実現するために、約16ミリ秒ごとにanimate関数が呼び出されます。

以上で実装完了です。

次回も引き続き別のパターンを作っていく予定です。