本文へスキップ
HELLO!
HIGASHIYUKI®.COM
0   /   100
カーソル

カーソルを少し遅れて追いかける輪

デモ

操作:カーソルを動かす。 「さわってみる」を押すと自動再生が止まり、マウスで動かせます。

仕様

用途
カーソル
動作
DOM
種類
レシピ
動作確認
three 0.186.0・gsap 3.15.0
公開日

しくみと調整

独自カーソルは、遅れずに付いてくる小さな点と、少し遅れて追いかける大きな輪の 2 つで作ります。どちらも画面に固定して置き、pointer-events を none にして、下のリンクやボタンを押せるようにします。ポインタが動くたびに、点は位置をそのまま書き込み、輪は quickTo でその位置へ向かわせます。

輪が追いつく時間は 0.45 秒にしています。0.3 秒ではほとんど点と重なって遅れが見えず、0.6 秒を超えると輪がふわふわ漂って、どこを指しているのか読み取りにくくなります。終わりのゆるやかな power3 と組み合わせると、止まった瞬間に吸い付くように収まります。

本物のカーソルは cursor: none で消しますが、消すのは独自カーソルを実際に出したときだけにします。指で触る端末や、スクリプトが動かなかったときまで消すと、画面に何も指すものが無くなります。中心をポインタに合わせる指定も忘れやすく、抜けると点と輪が右下へずれて重なります。

つまみは、輪の直径と追いつく時間の 2 つです。輪を大きく遅くするとゆったりした作品集の雰囲気になり、小さく速くすると操作の手応えが素直になります。リンクの上で輪だけを膨らませる、色を変える、といった足し方もよく合います。ポートフォリオやスタジオのサイト、キャンペーンページに向いています。

コード

HTML
<!-- 独自カーソル。飾りなので読み上げからは外し、<body> の終わりに置く -->
<span class="cursor-ring" aria-hidden="true"></span>
<span class="cursor-dot" aria-hidden="true"></span>
CSS
:root {
  --cursor-dot: 14px;
  --cursor-ring: 68px;
  --cursor-ink: #eeece6;   /* 輪の線 */
  --cursor-accent: #d4ff3a; /* 点 */
}

.cursor-dot,
.cursor-ring {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 9999;
  border-radius: 50%;
  /* 下のリンクやボタンを押せるようにする */
  pointer-events: none;
  /* JS がつないだときだけ出す。指で触る端末やスクリプトが動かないときは本物のカーソルのまま */
  display: none;
}

.cursor-dot {
  width: var(--cursor-dot);
  height: var(--cursor-dot);
  background: var(--cursor-accent);
}

.cursor-ring {
  width: var(--cursor-ring);
  height: var(--cursor-ring);
  box-sizing: border-box;
  border: 2px solid var(--cursor-ink);
}

.has-custom-cursor .cursor-dot,
.has-custom-cursor .cursor-ring {
  display: block;
}

/* 本物のカーソルは、独自カーソルを出したときだけ隠す */
.has-custom-cursor,
.has-custom-cursor a,
.has-custom-cursor button {
  cursor: none;
}
JavaScript
import { gsap } from "gsap";

const dot = document.querySelector(".cursor-dot");
const ring = document.querySelector(".cursor-ring");
// 指で触る端末にはポインタが無いので、独自カーソルは出さず本物のカーソルのままにする
const canHover = window.matchMedia("(hover: hover) and (pointer: fine)").matches;
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;

if (dot && ring && canHover) {
  // 中心をポインタに合わせる。輪の大きさを変えても位置がずれない
  gsap.set([dot, ring], { xPercent: -50, yPercent: -50 });
  document.documentElement.classList.add("has-custom-cursor");

  // 動きを減らす設定では、輪も遅れずに付いてくる
  const duration = reduceMotion ? 0 : 0.45;
  const ringX = gsap.quickTo(ring, "x", { duration, ease: "power3" });
  const ringY = gsap.quickTo(ring, "y", { duration, ease: "power3" });

  window.addEventListener("pointermove", (event) => {
    gsap.set(dot, { x: event.clientX, y: event.clientY });
    ringX(event.clientX);
    ringY(event.clientY);
  });

  // ページの外へ出たら消す(端に貼り付いたまま残らないように)
  document.addEventListener("pointerleave", () => {
    gsap.to([dot, ring], { autoAlpha: 0, duration: 0.2 });
  });
  document.addEventListener("pointerenter", () => {
    gsap.to([dot, ring], { autoAlpha: 1, duration: 0.2 });
  });
}

インタラクションデザインについて、
お気軽にご相談ください。

ここに並べているのは、Web サイトの中で動く UI のサンプルです。新しく作るサイトに組み込むことも、いま公開中のサイトに、ボタンやヒーロー、スクロールの演出だけを足すこともできます。設計から実装までの進め方はWeb サイト制作、端末ごとの見え方や表示速度の考え方はレスポンシブウェブサイトのページにまとめています。映像として作る動きをお探しの場合はモーショングラフィックスをご覧ください。