マウスストーカーが盛んに取り上げられるようになって早数年。さすがにそろそろやり方くらいは……と思い、試してみることにしました。
コード
まずは成果物を。
- 要素ホバーで拡大: Home – Vampire Stoker
- 要素ホバーで背景画像表示: Home – Vampire Stoker
最初の方(要素ホバーで拡大)は参考コードほぼそのままです。次のものはマウスストーカーが大きくなるだけでなく、背景画像をスポットライト的に切り抜いて表示する機能も付けたものになります。
リポジトリ。
HTML
<div class="c-mouseStalker_wrapper">
<div class="c-mouseStalker">
<div class="c-mouseStalker_cursor" id="c-mouseStalker_cursor"></div>
<div class="c-mouseStalker_delay" id="c-mouseStalker_delay"></div>
</div>
</div>
HTMLはいたってシンプル。カーソル用の要素とディレイがかかって追いかけてくる要素の2つの div
を用意します。
今回はさらにラッパーで覆って overflow: hidden;
をかけることで、画面端にカーソルが移動した際にディレイ要素の大きさ分だけはみ出てスクロールバーが表示されないようにしました。
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/latest/TweenMax.min.js" defer></script>
それから、今回はサクッと試すために CDN で TweenMax を読み込むようにしました。
css(Scss)
body {
position: relative;
// 元々のマウスカーソルを消す
cursor: none;
}
.c-mouseStalker {
// イベント反応させなくする
pointer-events: none;
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
&_wrapper {
// イベント反応させなくする
pointer-events: none;
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
// 端にカーソルを持って行ったときにディレイの部分だけはみ出てスクロールバーが表示されてしまうのを抑止
overflow: hidden;
}
&_cursor,
&_delay {
position: absolute;
top: 0;
left: 0;
pointer-events: none; // イベント反応させなくする
}
&_cursor {
width: 0.5rem;
height: 0.5rem;
background-color: f.$color;
z-index: 10001;
border-radius: 50%;
}
&_delay {
$delayRadius: 100px / 4;
width: 100%;
height: 100%;
background-color: f.$main-color;
z-index: 10000;
transition: clip-path ease 0.1s; // 遅くするともっさりした感じになる
opacity: 0.4;
// at 以降は円の中心の定義
clip-path: ellipse($delayRadius $delayRadius at 50% 50%);
&.active {
$activeTimes: 4;
background: {
image: url("../img/img.jpg");
attachment: fixed;
size: cover;
position: center center;
}
opacity: 1;
clip-path: ellipse($delayRadius * $activeTimes $delayRadius * $activeTimes at 50% 50%);
}
}
}
body
のcursor: none;
: デフォルトのマウスカーソルを非表示に.c-mouseStalker
と.c-mouseStalker_wrapper
:pointer-events: none;
- どちらも表示画面サイズいっぱいまで拡大
.c-mouseStalker_wrapper
:overflow: hidden;
で端にカーソルを持って行ったときにディレイの部分だけはみ出てスクロールバーが表示されてしまうのを抑止.c-mouseStalker_delay
: 肝- 要素自体は透明。
clip-path
で切り取った円形の範囲のみ見える状態にする- デフォルトは単色半透明のマスク
- 特定要素にホバーしたとき、 jQuery でクラス付与 (ホバー解除時にクラスも削除)
- クラスが付与されたとき、背景を単色から
background-image
で指定した画像にする- 背景画像は
background-attachment
,background-size
,background-position
指定あり
- 背景画像は
- クラスが付与されたとき、背景を単色から
transition: clip-path ease 0.1s;
でホバー時の拡大縮小等の切り替えを easing0.3s
にすると TweenMax のディレイも相まってもっさりした動きになってしまうので、0.1s
で
- 要素自体は透明。
JavaScript (jQuery + TweenMax)
// mouse stalker
const mouseStalker = () => {
const $cursor = $('#c-mouseStalker_cursor');
const $delay = $('#c-mouseStalker_delay');
const paramsArray = {
cursor: {
width: $cursor.outerWidth(),
coorX: 0,
coorY: 0,
delay: 0.001,
},
delay: {
width: $delay.outerWidth(),
coorX: 0,
coorY: 0,
delay: 6,
},
};
const activeClass = 'active';
let clipRadius = 100 / 4;
let clipScale = 1;
let clipPathCoor = `ellipse(${clipRadius * clipScale}px ${clipRadius * clipScale}px at 50% 50%)`;
// カーソルの遅延アニメーション
// ほんの少しだけ遅延させる (0.001秒)
TweenMax.to(
{},
paramsArray.cursor.delay,
{
repeat: -1,
onRepeat: function() {
paramsArray.delay.coorX += (paramsArray.cursor.coorX - paramsArray.delay.coorX) / paramsArray.delay.delay;
paramsArray.delay.coorY += (paramsArray.cursor.coorY - paramsArray.delay.coorY) / paramsArray.delay.delay;
clipPathCoor = `ellipse(${clipRadius * clipScale}px ${clipRadius * clipScale}px at ${paramsArray.delay.coorX}px ${paramsArray.delay.coorY}px)`;
// delay
TweenMax.set(
$delay,
{
css: {
clipPath: clipPathCoor,
}
}
);
// cursor
TweenMax.set(
$cursor,
{
css: {
left: paramsArray.cursor.coorX - (paramsArray.cursor.width / 2),
top: paramsArray.cursor.coorY - (paramsArray.cursor.width / 2),
}
}
);
}
}
);
// mouse hover
$('#hoverElmID').on({
mouseenter: function () {
$cursor.addClass(activeClass);
$delay.addClass(activeClass);
clipScale = 4;
},
mouseleave: function () {
$cursor.removeClass(activeClass);
$delay.removeClass(activeClass);
clipScale = 1;
},
});
// get mouse coordinate
$(document).on('mousemove', function (e) {
paramsArray.cursor.coorX = e.pageX;
paramsArray.cursor.coorY = e.pageY;
});
};
window.addEventListener('load', () => {
mouseStalker();
});
- だいたいサンプルコードに則ったコード
- カーソルとディレイ要素の各種パラメータは別の変数で持っていると個人的に分かりづらかったのでオブジェクトにまとめました
- 変数
clipPathCoor
で指定した値が実際のclip-path
で切り取られるディレイ要素の指定- カーソル用はほぼ弄らず
- ディレイ要素の方は TweenMax の
to
で指定するパラメータがtop
,left
からclip-path
のみに変更
- 特定要素のホバー時に上述 Scss の指定が反映されるようにクラスの付け外しを実施
- 同時に
clip-path
で切り取られる範囲を拡大縮小(拡大率の数値を変更している)
- 同時に
今回はこれで望んだ挙動になりました。
参考
マウスストーカー
- イケてるマウスカーソルを簡単に実装する | 株式会社 エヴォワークス -EVOWORX-
- Googleも推奨!アニメーションライブラリTweenMaxの使い方 入門編 | un-Tech
- Licensing – GreenSock
- Standard License – GreenSock
背景画像切り抜き
- JavaScript – マウスストーカーを透明のマスクにして背景を動的に切り取りたいのですが、どうすれば良いでしょうか?|teratail
- – CSS: カスケーディングスタイルシート | MDN
- clip-path-CSSリファレンス
- CSS animation で遊び倒す – CSS Clip Path- – Qiita