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

カーソル

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

デモ

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

仕様

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

しくみと調整

小さな点はポインタにぴたりと付き、大きな輪だけが0.45秒遅れて追いかけて、止まった瞬間に点のまわりへ吸い付くように収まります。どちらも画面に固定し、pointer-events を none にしてあるので、下のリンクやボタンはそのまま押せます。動くたびに点には位置をそのまま書き込み、輪は quickTo でそこへ向かわせているだけです。

遅れがはっきり見えて、しかも指している場所を見失わない長さとして、0.45秒を選びました。0.3秒では輪がほとんど点と重なって遅れが見えず、0.6秒を超えると輪が漂って、どこを指しているのか読み取りにくくなります。終わりがゆるやかなイージング(power3)と組み合わせているので、止まったときの収まりがなめらかです。

本物のカーソルは cursor: none で隠しますが、隠すのは独自カーソルを実際に表示したときだけにしています。いつでも隠してしまうと、タッチ端末や、スクリプトが動かなかったときに、指し示すものが何もなくなります。忘れやすいのが中心合わせで、要素の中心をポインタに合わせる指定が抜けると、点と輪がそれぞれ右下へずれて表示されます。

輪を大きく遅くすればゆったりした作品集の雰囲気に、小さく速くすれば手応えの素直なカーソルになります。リンクの上で輪だけを膨らませる、色を変えるといった演出も、この上にそのまま足していけます。

コード

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: #c8bf83; /* 点 */
}

.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");

  // 動きを減らす設定では、輪も遅れずに付いてくる(quickTo は duration が 0 だと値が動かない)
  const follow = (prop) => (reduceMotion ? (value) => gsap.set(ring, { [prop]: value }) : gsap.quickTo(ring, prop, { duration: 0.45, ease: "power3" }));
  const ringX = follow("x");
  const ringY = follow("y");

  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 サイト制作、端末ごとの見え方や表示速度の考え方はレスポンシブウェブサイトのページにまとめています。映像として作る動きをお探しの場合はモーショングラフィックスをご覧ください。