mousemove処理を軽く滑らかに!GSAPで作る2種類の追従マウスストーカー
サイトをオシャレに演出する「マウスストーカー」。 JavaScriptの mousemove イベントを使って実装するのが一般的ですが、マウスが動くたびに(1秒間に何十回も)処理が走るため、書き方によってはサイトの動作が重くなる原因になってしまいます。
そこで大活躍するのが、GSAPに用意されている gsap.quickTo() と gsap.quickSetter() という2つのメソッドです!これらは頻繁な値の更新を極限まで最適化してくれる、マウス追従アニメーション実装にとても便利なメソッドです!
今回は、この2つのメソッドの違いを解説しつつ、「瞬時に追従するカーソル」と「少し遅れて追従するカーソル」を同時に動かすデモをご用意しました。ホバー時のアクションも実装しているので、ぜひPC環境でマウスを動かして確かめてみてください!
デモ動作
See the Pen GSAP quickTo & quickSetterで作るマウスストーカー by mikanbako (@mikanbako) on CodePen.
demo: GSAPで作るマウスストーカー
実装コードの紹介
【HTML】
<div class="l-wrap">
<h2>マウスストーカー デモ</h2>
<p class="p-desc">PCからマウスを動かして動作を確認してください。<br>ボタンにホバーするとカーソルが変化します!</p>
<div class="p-btn-area">
<button class="c-btn js-hover-target">Hover Me!</button>
</div>
<!-- マウスストーカー要素(スマホでは非表示になります) -->
<!-- ① quickSetter用(瞬時に付いてくる小さなドット) -->
<div class="c-cursor-dot js-cursor-dot"></div>
<!-- ② quickTo用(遅れて付いてくる大きな円) -->
<div class="c-cursor-circle js-cursor-circle"></div>
</div>
【CSS】
.l-wrap {
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
color: #3e3e3e;
padding: 20px;
}
h2 {
font-size: 16px;
color: #1bb4d4;
margin-bottom: 16px;
}
.p-desc {
font-size: 16px;
font-weight: bold;
color: #666;
margin-bottom: 40px;
}
/* ホバー対象のボタン */
.c-btn {
padding: 16px 32px;
font-size: 18px;
font-weight: bold;
color: #fff;
background-color: #3e3e3e;
border-radius: 30px;
cursor: pointer;
}
/* =========================================
マウスストーカーの基本設定(最初は非表示)
========================================= */
.c-cursor-dot,
.c-cursor-circle {
display: none; /* デフォルト(スマホなど)では消しておく */
position: fixed;
top: 0;
left: 0;
border-radius: 50%;
pointer-events: none; /* カーソル要素がクリックの邪魔をしないように必須! */
z-index: 9999;
}
/* =========================================
【重要】PC(ホバー可能な環境)のみ適用するスタイル
========================================= */
@media (hover: hover) and (pointer: fine) {
/* マウスストーカーを表示する */
.c-cursor-dot,
.c-cursor-circle {
display: block;
}
/* ① quickSetter用 */
.c-cursor-dot {
width: 8px;
height: 8px;
background-color: #c72c4c;
}
/* ② quickTo用 */
.c-cursor-circle {
width: 40px;
height: 40px;
background-color: rgba(27, 180, 212, 0.4);
}
}
【JavaScript】
const cursorDot = document.querySelector('.js-cursor-dot');
const cursorCircle = document.querySelector('.js-cursor-circle');
const hoverTargets = document.querySelectorAll('.js-hover-target');
const mm = gsap.matchMedia();
// PC(マウス操作)環境の時だけGSAPの処理を実行
mm.add("(hover: hover) and (pointer: fine)", () => {
// GSAPを使って要素の中心をマウス位置に合わせる!
gsap.set([cursorDot, cursorCircle], { xPercent: -50, yPercent: -50 });
// ① quickSetter の準備(瞬時に値をセットする)
// 使い方: gsap.quickSetter(ターゲット, "プロパティ", "単位")
const setDotX = gsap.quickSetter(cursorDot, "x", "px");
const setDotY = gsap.quickSetter(cursorDot, "y", "px");
// ② quickTo の準備(アニメーションしながら追従する)
// 使い方: gsap.quickTo(ターゲット, "プロパティ", { アニメーション設定 })
const setCircleX = gsap.quickTo(cursorCircle, "x", { duration: 0.6, ease: "power3.out" });
const setCircleY = gsap.quickTo(cursorCircle, "y", { duration: 0.6, ease: "power3.out" });
// マウスが動くたびに座標を更新
window.addEventListener('mousemove', (e) => {
// 画面の左上からの座標(clientX, clientY)を取得
const mouseX = e.clientX;
const mouseY = e.clientY;
// quickSetterを実行(アニメーションなしで瞬時に移動)
setDotX(mouseX);
setDotY(mouseY);
// quickToを実行(0.6秒かけて滑らかに移動)
setCircleX(mouseX);
setCircleY(mouseY);
});
// ボタンにホバーした時のアクション(ストーカー要素を拡大&背景色変化)
hoverTargets.forEach(target => {
target.addEventListener('mouseenter', () => {
gsap.to(cursorCircle, {
scale: 1.5,
backgroundColor: "rgba(199, 44, 76, 0.4)",
duration: 0.3,
ease: "power2.out"
});
});
target.addEventListener('mouseleave', () => {
gsap.to(cursorCircle, {
scale: 1, // 元のサイズに戻す
backgroundColor: "rgba(27, 180, 212, 0.4)", // 元の色に戻す
duration: 0.3,
ease: "power2.out"
});
});
});
});
コードの解説・違いまとめ
PC環境限定にするメディアクエリの活用
/* =========================================
【重要】PC(ホバー可能な環境)のみ適用するスタイル
========================================= */
@media (hover: hover) and (pointer: fine) {
...
}
// PC(マウス操作)環境の時だけGSAPの処理を実行
const mm = gsap.matchMedia();
mm.add("(hover: hover) and (pointer: fine)", () => {
...
});
マウスストーカーはスマートフォンなどのタッチデバイスでは不要(むしろ邪魔)な機能です。 CSSでは @media (hover: hover) and (pointer: fine) を使い、JSではGSAPの gsap.matchMedia() を使うことで、「マウス(高精度なポインター)が使える環境でのみ」ストーカーを動作させるという安全な実装にしています。
gsap.quickSetter():アニメーションなしで即座に反映
// ① quickSetter の準備(瞬時に値をセットする)
// 使い方: gsap.quickSetter(ターゲット, "プロパティ", "単位")
const setDotX = gsap.quickSetter(cursorDot, "x", "px");
const setDotY = gsap.quickSetter(cursorDot, "y", "px");
// マウスが動くたびに座標を更新
window.addEventListener('mousemove', (e) => {
// 画面の左上からの座標(clientX, clientY)を取得
const mouseX = e.clientX;
const mouseY = e.clientY;
// quickSetterを実行(アニメーションなしで瞬時に移動)
setDotX(mouseX);
setDotY(mouseY);
});
中央の小さな赤いドットに使用しています。 これは duration などのアニメーション要素を持たず、渡された数値を対象要素に直接セットするだけのメソッドです。通常の gsap.to( { duration: 0 } ) よりも内部処理が省かれているため、mousemoveイベントのような超高速で呼ばれる処理に最適です。
gsap.quickTo():滑らかなイージングで追従
// ② quickTo の準備(アニメーションしながら追従する)
// 使い方: gsap.quickTo(ターゲット, "プロパティ", { アニメーション設定 })
const setCircleX = gsap.quickTo(cursorCircle, "x", { duration: 0.6, ease: "power3.out" });
const setCircleY = gsap.quickTo(cursorCircle, "y", { duration: 0.6, ease: "power3.out" });
// マウスが動くたびに座標を更新
window.addEventListener('mousemove', (e) => {
// 画面の左上からの座標(clientX, clientY)を取得
const mouseX = e.clientX;
const mouseY = e.clientY;
// quickToを実行(0.6秒かけて滑らかに移動)
setCircleX(mouseX);
setCircleY(mouseY);
});
外側の遅れてくる水色の円に使用しています。 こちらは quickSetter のパフォーマンスの良さに加えて、「指定したイージングや秒数でアニメーションする」機能を持っています。マウスをサッと動かした時に、スッと滑らかに追従するリッチな表現が、たった数行のコードで実現できます。
補足:公式ドキュメント
より詳しい仕様や最新の対応状況については、以下の公式ドキュメントをご参照ください。
まとめ
今回は、GSAPの quickSetter と quickTo を使った、パフォーマンスに優しいマウスストーカーの実装をご紹介しました。
この2つを組み合わせることで、「ポインターの正確な位置を示すドット」と、「視覚的に楽しい演出を加える遅延サークル」という、Webサイトでよく見る表現が簡単に作れます。
ホバー対象のボタンの mouseenter をトリガーに、カーソルの見た目を切り替える工夫と組み合わせれば、よりインタラクティブなUIを作れるので、ぜひご自身のサイトにも組み込んでみてください!