😎

jsで「瞬間移動しない」マウスストーカー作成

2023/10/16に公開

はじめに

はてな教科書の課題にあったので作成してみた。

ただ、mousemoveだけでは画面外から入ってきたときに瞬間移動するのでカスタマイズしました。

コード

speed変数は無視しても大丈夫です。
ちなみに、speedは10以上に設定するとかくかくします。

document.addEventListener("DOMContentLoaded", () => {
  let circle = document.getElementById("circle");

  let mouseMoveElement = new MouseMoveElement(circle);
  // 画面全体にmousemoveイベント追加
  document.addEventListener("mousemove", (e) => {
    // マウスが動くたびにイベントを発行
    mouseMoveElement.moveNextPoint(e.pageX, e.pageY, 1);
  });
});

class MouseMoveElement {
  constructor(element) {
    this.element = element;

    // ノードの初期位置を取得
    this.oldPointX = element.offsetTop;
    this.oldPointY = element.offsetLeft;

    element.style.position = "absolute";

    // ノードの中心をカーソルの先に合わせる
    this.halfWidth = element.clientWidth / 2;
    this.halfHeight = element.clientHeight / 2;

    this.intervalId = 0;
    this.count = 0;
  }

  moveNextPoint(newPointX, newPointY, speed = 1) {
    this.count = 0;
    // x軸の距離
    let xDiff = (newPointX - this.oldPointX) / speed;

    // y軸の距離
    let yDiff = (newPointY - this.oldPointY) / speed;

    // xとyの距離の長いほうを取得
    let maxDiff = parseInt(Math.max(Math.abs(xDiff), Math.abs(yDiff)));

    // インターバルごとに動く距離を計算
    // ステップ数をそろえるために、大きいほうで割る
    // 距離が長い方は1px
    // 短いほうは1px以下 で移動
    let xStep = (xDiff / maxDiff) * speed;
    let yStep = (yDiff / maxDiff) * speed;

    // マウスが動くたびに動く距離と方向を更新するために
    // 前に設定された距離と方向をキャンセル
    clearInterval(this.intervaId);
    
    // setIntervalで瞬間移動を防ぐ
    this.intervaId = setInterval(() => {
      // 大きいほうの距離をステップ数として
      // そこに到達するまでは移動し続け、カウントする
      if (this.count < maxDiff) {
        // 前の位置からステップの距離を移動
        this.element.style.left =
          this.oldPointX + xStep - this.halfWidth + "px";
        this.element.style.top =
          this.oldPointY + yStep - this.halfHeight + "px";

        // 前の位置を更新
        this.oldPointX = this.oldPointX + xStep;
        this.oldPointY = this.oldPointY + yStep;
        this.count++;

        // カウントが総ステップ数に到達するとインターバルをキャンセルし、
	// 移動を止める
      } else {
        this.count = 0;
        clearInterval(this.intervaId);
      }
    }, 1);
  }
}
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>サンプル</title>
<style type="text/css">
#circle {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background-color: blue;
  z-index: -1;
}
</style>
<script src="./main.js"></script>
</head>
<body>
<div type="text" id="circle"></div>
</body>
</html>

おわりに

はじめsetTimeoutを使用していてスムーズに動かなくてハマった。
次は、距離によって加速や減速できるようにしたい。

Discussion