<p class="caution">※モバイル端末でのtouchイベントのみ対応。<br>click/mouseイベントは発火しません。</p>

<div id="joystick" class="joystick">
  <div id="joystick-frame" class="joystick-frame">
    <div id="joystick-ball" class="joystick-ball"></div>
  </div>
</div>
body {
  position: fixed;
  background: #8cafea;
}
.caution {
  position: fixed;
  margin: auto;
  text-align: center;
  width: 100%;
  font-size: 0.85rem;
  color: white;
}
.joystick {
  position: fixed;
  bottom: 0.5rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 10;
}
.joystick-frame {
  width: 130px;
  height: 130px;
  border-radius: 100rem;
  border: 2px white solid;
  position: relative;
}
.joystick-ball {
  width: 60px;
  height: 60px;
  background: white;
  border-radius: 100rem;
  border: 2px white solid;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}
let joystickBall;
let joystickCenterX;
let joystickCenterY;
let joystickLimitNumber = 35;

const init = () => {
  setupJoystick();
};

const setupJoystick = () => {
  joystickBall = document.getElementById("joystick-ball");
  joystickCenterX =
    joystickBall.getBoundingClientRect().left + joystickBall.clientWidth / 2;
  joystickCenterY =
    joystickBall.getBoundingClientRect().top + joystickBall.clientHeight / 2;
  joystickBall.addEventListener("touchmove", dragMove);
  joystickBall.addEventListener("touchend", dragLeave);
};

const dragMove = (event) => {
  event.preventDefault();

  const pageX = event.touches[0].pageX;
  const pageY = event.touches[0].pageY;

  let touchX =
    Math.abs(pageX - joystickCenterX) < joystickLimitNumber
      ? pageX - joystickCenterX
      : pageX - joystickCenterX > 0
      ? joystickLimitNumber
      : -joystickLimitNumber;

  let touchY =
    Math.abs(pageY - joystickCenterY) < joystickLimitNumber
      ? pageY - joystickCenterY
      : pageY - joystickCenterY > 0
      ? joystickLimitNumber
      : -joystickLimitNumber;

  joystickBall.style.left = `calc(50% + ${touchX}px)`;
  joystickBall.style.top = `calc(50% + ${touchY}px)`;
};

const dragLeave = () => {
  joystickBall.style.top = "50%";
  joystickBall.style.left = "50%";
};

init();

window.addEventListener("resize", () => {
  joystickCenterX =
    joystickBall.getBoundingClientRect().left + joystickBall.clientWidth / 2;
  joystickCenterY =
    joystickBall.getBoundingClientRect().top + joystickBall.clientHeight / 2;
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/three.js/0.148.0/three.min.js