<div>
  <button class="sticky">♠️</button>
  <button class="sticky">♣️</button>
  <button class="sticky">♦️️</button>
  <button class="sticky">♥️</button>
</div>
* {
  cursor: none !important;
}

button {
  font-size: 40px;
  width: 60px;
  height: 60px;
  border-width: 0px;
  background-color: transparent;
  margin: 5px;
  display: inline-flex;
  justify-content: center;
  align-items: center;
}
import gsap, { Power2 } from "https://cdn.skypack.dev/gsap";

const pointer = document.createElement("div");

// 見た目を整える
pointer.style.width = "20px";
pointer.style.height = "20px";
pointer.style.borderRadius = "50%";
pointer.style.backgroundColor = "gray";
pointer.style.opacity = "0.5";

// 他の要素の上に表示されるように `z-index` の値を上げる
pointer.style.zIndex = "100000";

// 初期位置をページの真ん中にする
pointer.style.position = "absolute";
pointer.style.transform = "translate(-50%, -50%)";
pointer.style.top = "50%";
pointer.style.left = "50%";

// ポインターに対してのマウスイベントを透過させる
pointer.style.pointerEvents = "none";

// ページに表示する
document.body.append(pointer);

window.addEventListener("mousemove", (event) => {
  const { pageX, pageY, clientX, clientY } = event;

  // マウス下の要素一覧を取得
  const elements = document.elementsFromPoint(clientX, clientY);
  // `sticky` クラスが付いている要素を探す
  const target = elements.find((el) => el.classList.contains("sticky"));

  if (target) {
    // sticky要素があった時はポインターを要素と同じ場所・大きさに変形させる
    const rect = target.getBoundingClientRect();
    const top = rect.top + rect.height / 2;
    const left = rect.left + rect.width / 2;
    const { width, height } = rect;
    const borderRadius = Math.min(rect.height, rect.width) * 0.1;

    gsap.to(pointer, {
      top,
      left,
      width,
      height,
      borderRadius,
      duration: 0.1,
      ease: Power2.easeOut,
      overwrite: true
    });
  } else {
    // sticky要素がない場合は元の形状に戻す
    gsap.to(pointer, {
      width: 20,
      height: 20,
      borderRadius: "50%",
      duration: 0.1,
      ease: Power2.easeOut,
      overwrite: true
    });

    // マウス座標に移動させる
    gsap.to(pointer, {
      top: pageY,
      left: pageX,
      duration: 0.05,
      ease: Power2.easeOut
    });
  }
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.