Blog

ブログ

マウスストーカーの作り方を解説(Vanilla JSで)

前田 大地

私たちセブンシックスのオフィシャルサイトに気まぐれでマウスストーカーを実装しました。マウスカーソルの動きにあわせて後ろからついてくるアレです。実装にあたって色々と試行錯誤しましたので、せっかくですから共有したいと思います。

完成形

まずは完成形がこちら。丸いヤツがマウスを追いかけてきて、リンクなどの要素上にマウスを乗せると形が変化します。

See the Pen mousest_3 by Daichi Maeda (@daichifive) on CodePen.

マウスストーカーとは?

マウスの動きに合わせてついてくるヤツのことをマウスストーカーと呼ぶそうです。なんだかネガティブな感じのするネーミングですね。名前のとおり、どれだけ全力でマウスを動かして振り切っても諦めずに追いかけてきます、こわっ。

どんな役割?

これ、なんの意味があるんでしょうね。なんか分からないけどちょっとカッコいい的なポジションなのでしょうか、ストーカーのくせに。もちろんマウスの位置だったり状態が直感的に分かるという機能的な一面もあり、ちょっとうっとうしいけど憎めないヤツなんだと思います、はい。

どうやって実装するの?

HTML、CSS、JavaScriptを組み合わせて実装します。マウスポインターを追いかけるために、JavaScriptを使って今どこにマウスがいるのか位置情報を調べたりします、こわっ。

いざ実装

早速、マウスストーカーを作っていきましょう。

HTMLでマウスストーカーとなる要素を作成

後のマウスストーカーとなる要素を、bodyの子要素として配置します。まだ今のところストーキングしません。divの中にdivを入れているのは、JavaScriptからtransformを操作する関係上、子要素があると形状を変えたりするのに都合良いからです。

<div id="g-ms" class="g-ms">
  <div class="g-ms_i"></div>
</div>

マウスストーカーのCSSを設定

マウスストーカーとなる親要素に対して、以下のようにCSSを設定します。

.g-ms {
  position: fixed;
  top: 0;
  left: 0;
  transform: translate3d(50vw, 50vh, 0);
  z-index: 9999;
  pointer-events: none;
  transition: all 0.3s ease-out;
}

マウスストーカーは画面内を縦横無尽に駆け巡るので、通常のレイアウトとは切り離すために「position: fixed;」にします。画面上の位置は、JavaScriptでtransformの値を操作して動かしますので、とりあえず初期位置が画面中央になるようにx方向は50vh、y方向は50vwとしています。他の要素に隠れてしまわないようにz-indexを調整して、クリックの邪魔にならないようにpointer-eventsをnoneにします。transitionを指定すると、動きがちょびっとかっちょよくなります。

マウスストーカーの見た目を設定

次に、子要素を使ってマウスストーカーの見た目を調整します。

.g-ms_i {
  width: 60px;
  height: 60px;
  margin: -30px 0 0 -30px;
  border-radius: 30px;
  background: rgba(0,0,0,.05);
}

半径30pxの半透明な円形ですね。親要素「.g-ms」の左上がマウスポインターの位置と重なる部分になりますので、marginを使って円の中心位置をずらしています。

動かす

JavaScriptでマウスポインターの位置を取得して、transformで動かします。いろいろなサイトのブログ記事を見ましたが、どうせならrequestAnimationFrameを使ってみたい!と、いうことで、こちらの記事(マウスカーソルにゆるゆると追従するマウスストーカーの実装方法|やくだつブログ)でご紹介されている方法が、解説も丁寧なのでおすすめです。ほとんどそのまま使ってごめんなさい、ありがとうございます。

const mouseStalker = document.getElementById('g-ms');
let msPos = {
  // マウスストーカーの位置
  s: {
    x: document.documentElement.clientWidth / 2,
    y: document.documentElement.clientHeight / 2
  },
  // マウスポインターの位置
  m: {
    x: document.documentElement.clientWidth / 2,
    y: document.documentElement.clientHeight / 2
  }
};

// マウスポインターの位置取得
document.addEventListener('mousemove', function(e){
  msPos.m.x = e.clientX;
  msPos.m.y = e.clientY;
});

// アニメーション開始
requestAnimationFrame(msPosUpdate);

// マウスストーカーを動かす関数
function msPosUpdate() {
  msPos.s.x += (msPos.m.x - msPos.s.x) * 0.1;
  msPos.s.y += (msPos.m.y - msPos.s.y) * 0.1;
  const x = Math.round(msPos.s.x * 10) / 10;
  const y = Math.round(msPos.s.y * 10) / 10;
  mouseStalker.style.transform = `translate3d(` + x + 'px,' + y + 'px, 0)';
  requestAnimationFrame(msPosUpdate);
}

動いた

とりあえず普通に動いてますね。

See the Pen mousest_1 by Daichi Maeda (@daichifive) on CodePen.

マウスデバイスのときだけ表示させる

スマホなどのタッチデバイス時、マウスストーカーは不要なので非表示にしたいです。

先ほどのJavaScriptを書き直して、マウスストーカーをアクティブにする処理を加えます。アクティブになったときに「.g-ms-active」というclassを付与することで、CSS側での表示調整ができるようにします。

下記のコードでは、pointer:fineかつmousemoveを検出したときに、マウスストーカーがアクティブになります。私はこのコードを書いているとき、軽いトランス状態だったためか、なぜ、pointer:fineとmousemoveを組み合わせる必要があったのか憶えていません。pointer:fineだけでも問題ない気がするのですが、何らかの理由があったのかもしれません、もう記憶ないから真相は闇の中ですけど。

const mouseStalker = document.getElementById('g-ms');
let msPos = {
  // マウスストーカーの位置
  s: {
    x: document.documentElement.clientWidth / 2,
    y: document.documentElement.clientHeight / 2
  },
  // マウスポインターの位置
  m: {
    x: document.documentElement.clientWidth / 2,
    y: document.documentElement.clientHeight / 2
  }
};

// マウスストーカーをactiveにする
if (window.matchMedia( "(pointer: fine)" ).matches) {
  document.addEventListener ("mousemove", msActivate);
}
function msActivate() {
  mouseStalker.classList.add('g-ms-active');
  document.removeEventListener ("mousemove", msActivate);
  // mouseの位置セット
  document.addEventListener('mousemove', function(e){
    msPos.m.x = e.clientX;
    msPos.m.y = e.clientY;
  });
  // アニメーション開始
  requestAnimationFrame(msPosUpdate);
}

// マウスストーカーを動かす関数
function msPosUpdate() {
  msPos.s.x += (msPos.m.x - msPos.s.x) * 0.1;
  msPos.s.y += (msPos.m.y - msPos.s.y) * 0.1;
  const x = Math.round(msPos.s.x * 10) / 10;
  const y = Math.round(msPos.s.y * 10) / 10;
  mouseStalker.style.transform = `translate3d(` + x + 'px,' + y + 'px, 0)';
  requestAnimationFrame(msPosUpdate);
}

CSSは、マウスストーカーをはじめ透明にしておいて、アクティブになってから表示させるように修正します。

.g-ms {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 9999;
  transform: translate3d(50vw, 50vh, 0);
  pointer-events: none;
  transition: all 0.3s ease-out;
}

.g-ms_i {
  width: 60px;
  height: 60px;
  margin: -30px 0 0 -30px;
  border-radius: 30px;
  transition: all .3s ease;
  background: rgba(0,0,0,.05);
  opacity: 0;
}

.g-ms.g-ms-active .g-ms_i {
  opacity: 1;
}

で、できたのがこれ。

See the Pen mousest_2 by Daichi Maeda (@daichifive) on CodePen.

マウスホバーで形状を変化

ただ追いかけてくるだけだと面白くないので、リンクとかにマウスをのせたとき、マウスストーカーの形状を変化させてみます。

マウスホバー時にclassを付与する

マウスホバー時「.g-ms-hover」というclassが付与されるよう、JavaScriptに追記します。今回は、a要素とbutton要素と、あとは任意に指定できるよう「.msl」というclassに反応するようにしました。

// hover時にclass追加
const stalkerLinkObj = document.querySelectorAll('a, button, .msl');
for (let i = 0; i < stalkerLinkObj.length; i++) {
  stalkerLinkObj[i].addEventListener('mouseover', function(){
    mouseStalker.classList.add('g-ms-hover');
  });
  stalkerLinkObj[i].addEventListener('mouseout', function(){
    mouseStalker.classList.remove('g-ms-hover');
  });
}

ホバー時のスタイルを追加

で、CSS側に「.g-ms-hover」が付与されたときのスタイルを追記します。このときのスタイルはCSSで自由に設定できますのでお好みでOKです。今回は、マウスストーカーがギュっと実体化されるようなイメージにしました。

body {
  /* exclusionに影響するので背景色はきちんと指定 */
  background: #fff;
}
.g-ms.g-ms-hover {
  mix-blend-mode: difference;
}
.g-ms.g-ms-hover .g-ms_i {
  background: #fff;
  transform: scale(.2);
}

完成

で、できたのがこれ。

See the Pen mousest_3 by Daichi Maeda (@daichifive) on CodePen.

やってみた感想

個人的にマウスストーカーってあんまり好きではないかも・・・。ちなみにセブンシックスのサイトでは、これにもうちょっと手を加えて、ページ読込中にぐるぐる回るスピナーへと形状変化させたりしています。面白がってやってみたけど、無駄にややこしくなりました。きっと、次回のリニューアル時には消えている予感。

単なるマウスストーカーとしてではなく、ホバーするリンクの内容に応じて「VIEW MORE」とか、「NEXT」とか、ユーザーのクリックを補助するメッセージを表示させるなどして、もうちょっと役立つ使い方をしたほうが建設的かもしれませんね。マウスカーソルというのは、ユーザーからの視線が常に注がれる特等席ですからね。あれ、なんか、私、今、ちょっと良いこと言いましたね。マウスカーソルはユーザーの注目が集まる特等席、です。はい。

Web Designer / Developer

前田 大地

沼津高専中退。デザイン会社、システム開発会社を経てセブンシックスを設立。マーケティング、デザイン、テクノロジーに精通するオールラウンダーとして、県内の中小企業に向けた戦略型ホームページ制作を開始。一方で、都内の広告代理店からの要請で大企業案件にも多数参加。企業が本当に必要とするホームページ制作とは何か、を日々探求している。

Blog top