要素に入ったときにポインターがプラスになり周囲のテキストが回転するパターンを実装してみる(備忘録)

今回も前回の記事の応用編で、要素に入ったときにポインターがプラスになり周囲のテキストが回転しているパターンを実装してみます。

周囲のテキストは、SVG画像です。FigmaのTo Pathというプラグインを用いて作成しています。

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 class="custom-cursor">
        <div class="plus-horizontal"></div>
        <div class="plus-vertical"></div>
      </div>
      <img src="./images/text-rotate.svg" alt="Rotating Text" class="rotate-text">
    </div>
  </main>
  <script src="./style.js"></script>
</body>

</html>

pointer-areaの中に、プラスアイコンを作るためのdivタグ、svg画像を記述しました。

ただこのやり方は、回転しているテキストに内容に修正が発生した場合、毎回作り直す手間が発生してしまうがめ、現実的なやり方ではないかもしれません。今の自分にはこのやり方くらいしか思い浮かばなかったため、妥協して対応しています。

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: 160px 0;
}

.pointer-area {
  background-color: #c3512f;
  height: 400px;
  cursor: none;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}

.custom-cursor {
  width: 80px;
  height: 80px;
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  visibility: hidden;
  opacity: 0;
  transform: scale(0);
  transition: transform 0.3s ease-in-out, opacity 0.3s, visibility 0.3s;
}

.plus-horizontal,
.plus-vertical {
  background-color: black;
  position: absolute;
}

.plus-horizontal {
  width: 30px;
  height: 1px;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.plus-vertical {
  width: 1px;
  height: 30px;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.rotate-text {
  width: 120px;
  height: 120px;
  position: absolute;
  animation: rotate 10s linear infinite;
  transform-origin: center;
  visibility: hidden;
  opacity: 0;
  transform: scale(0);
  transition: transform 0.3s ease-in-out, opacity 0.3s, visibility 0.3s;
}

@keyframes rotate {
  from {
    transform: rotate(0deg) scale(1);
  }

  to {
    transform: rotate(360deg) scale(1);
  }
}

プラスアイコンや円形のテキストを回転させるための設定をしています。要素に入る前の段階では、透明かつscaleも0にして表示されないようにしています。

JavaScriptの記述について

"use strict";

document.addEventListener("DOMContentLoaded", () => {
  // pointer-area要素とカスタムカーソル関連要素を取得
  const pointerArea = document.querySelector(".pointer-area");
  const cursor = document.querySelector(".custom-cursor");
  const rotateText = document.querySelector(".rotate-text");

  // ポインターエリアにマウスが入るとカスタムカーソルとテキストを表示
  pointerArea.addEventListener("mouseenter", () => {
    cursor.style.visibility = "visible";
    cursor.style.opacity = "1";
    cursor.style.transform = "scale(1)";
    rotateText.style.visibility = "visible";
    rotateText.style.opacity = "1";
    rotateText.style.transform = "scale(1) rotate(0deg)";
  });

  // マウスが動くたびにカスタムカーソルとテキストの位置を更新
  pointerArea.addEventListener("mousemove", (e) => {
    // カーソルの中心位置を計算するためのサイズ設定(カーソルサイズの半分)
    const cursorSize = 40;

    // pointer-area要素の現在の位置とサイズを取得
    const rect = pointerArea.getBoundingClientRect();

    // マウスカーソルがpointer-area内にあるかどうかをチェック
    // e.clientXとe.clientYは、ビューポートに対するマウスの絶対座標
    const isInsidePointerArea =
      e.clientX >= rect.left &&
      e.clientX <= rect.right &&
      e.clientY >= rect.top &&
      e.clientY <= rect.bottom;

    // マウスがポインターエリア内にある場合
    if (isInsidePointerArea) {
      // カスタムカーソルの位置を更新。ポインターの位置からpointer-areaの左上端までの距離を引いて、
      // カーソルの中心がマウスポインタになるように調整
      cursor.style.left = `${e.clientX - rect.left - cursorSize}px`;
      cursor.style.top = `${e.clientY - rect.top - cursorSize}px`;

      // 回転テキストの位置を更新。回転テキストが中心に来るようにさらに調整
      rotateText.style.left = `${e.clientX - rect.left - 60}px`;
      rotateText.style.top = `${e.clientY - rect.top - 60}px`;

      // カスタムカーソルと回転テキストを可視化
      cursor.style.visibility = "visible";
      cursor.style.opacity = "1";
      cursor.style.transform = "scale(1)";
      rotateText.style.visibility = "visible";
      rotateText.style.opacity = "1";
      rotateText.style.transform = "scale(1) rotate(0deg)";
    } else {
      // マウスがポインターエリア外に移動した場合、カスタムカーソルと回転テキストを非表示に
      cursor.style.visibility = "hidden";
      cursor.style.opacity = "0";
      cursor.style.transform = "scale(0)";
      rotateText.style.visibility = "hidden";
      rotateText.style.opacity = "0";
      rotateText.style.transform = "scale(0) rotate(0deg)";
    }
  });

  // ポインタエリアからマウスが離れたらカスタムカーソルとテキストを非表示に
  pointerArea.addEventListener("mouseleave", () => {
    cursor.style.visibility = "hidden";
    cursor.style.opacity = "0";
    cursor.style.transform = "scale(0)";
    rotateText.style.visibility = "hidden";
    rotateText.style.opacity = "0";
    rotateText.style.transform = "scale(0) rotate(0deg)";
  });
});

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

rotate-textに関する記述が複雑ですが、カーソルの中心がマウスポインタの位置に、回転テキストの位置が中心になるよう調整しています。

あとはCSSの設定した中身が変わるよう調整しています。

これで実証は完了です。

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