要素に入ってマウスを動かすとポインターがランダム画像に切り替わる実装をしてみる(備忘録)

今回も前回の記事の応用編で、要素に入ってマウスを動かすとポインターがランダム画像に切り替わる実装をしてみます。

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">
  <!-- Global site tag (gtag.js) - Google Analytics -->
  <script src="../inc_tag.js"></script>
</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;
  width: 90%;
  height: 560px;
  margin: 0 auto;
  cursor: none;
  position: relative;
  overflow: hidden;
}

.custom-cursor {
  width: 240px;
  height: 180px;
  position: absolute;
  pointer-events: none;
  background-size: cover;
  opacity: 1;
  transition: opacity 2s ease-out;
}

custom-cursorに対して、画像の縦横のサイズを設定しています。background-sizeをcoverにして要素全体を覆うようにしています。

JavaScriptの記述について

"use strict";

// ドキュメントが完全に読み込まれた後に実行される関数を設定
document.addEventListener("DOMContentLoaded", () => {
  // 指定されたクラス名'.pointer-area'を持つ要素を検索し、変数に格納
  const pointerArea = document.querySelector(".pointer-area");
  // pointerArea要素の現在の位置とサイズに関する情報を取得
  const rect = pointerArea.getBoundingClientRect();
  // 表示する画像のURLを配列に格納
  const images = [
    "images/img_01.webp",
    "images/img_02.webp",
    "images/img_03.webp",
    "images/img_04.webp",
    "images/img_05.webp",
  ];
  // 最後に画像を表示した時刻を記録する変数
  let lastImageTime = 0;
  // 画像が表示される最小間隔をミリ秒で指定
  const imageInterval = 50;

  // pointerArea要素に対してmousemoveイベントリスナーを設定
  pointerArea.addEventListener("mousemove", (e) => {
    // 現在の時刻を取得
    const currentTime = Date.now();
    // 最後に画像が表示されてから指定した間隔以上経過しているかを確認
    if (currentTime - lastImageTime >= imageInterval) {
      // 最後に画像を表示した時刻を更新
      lastImageTime = currentTime;
      // ランダムに画像を選択するためのインデックスを生成
      const imageIndex = Math.floor(Math.random() * images.length);
      // 新しいdiv要素を作成し、クラス名'custom-cursor'を追加
      const cursor = document.createElement("div");
      cursor.classList.add("custom-cursor");
      // div要素の背景画像としてランダムに選んだ画像を設定
      cursor.style.backgroundImage = `url(${images[imageIndex]})`;
      // div要素の位置をマウスカーソルの位置に設定(pointerAreaの左上端からの相対位置を考慮)
      cursor.style.left = `${e.clientX - rect.left - 120}px`;
      cursor.style.top = `${e.clientY - rect.top - 90}px`;
      // pointerAreaにdiv要素を追加
      pointerArea.appendChild(cursor);

      // 1秒後にdiv要素の不透明度を0に設定し、フェードアウト開始
      setTimeout(() => {
        cursor.style.opacity = 0;
        // フェードアウト完了後、さらに2秒後にdiv要素をDOMから削除
        setTimeout(() => cursor.remove(), 2000);
      }, 1000);
    }
  });
});

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

imageIntervalの数値は小さくすれば、マウスを移動したときにより細かく画像が切り替わるようになります。大きくすればその逆になります。

要素内でマウスを移動している間に、すでに表示された画像をゆっくりフェードアウトするようにするため、setTimeoutで対応しています。

これで実証は完了です。

次回も引き続き応用編で別のパターンを紹介していけたらと思います。