JSFiddle - React, Tailwind, and code Playground
HTML
<div id="main">
<div id="wrapper">
</div>
</div>
CSS
* {
border: 0;
box-sizing: border-box;
margin: 0;
padding: 0;
}
#main {
align-items: center;
display: flex;
height: 100vh;
justify-content: center;
margin: auto;
width: 100vw;
}
#wrapper {
border: 1px solid black;
width: 300px;
height: 300px;
}
.joystick {
background-color: blue;
border-radius: 100%;
cursor: pointer;
height: 100%;
user-select: none;
width: 100%;
}
JavaScript
const joystick = createJoystick(document.getElementById('wrapper'));
// setInterval(() => console.log(joystick.getPosition()), 16);
function createJoystick(parent) {
const maxDiff = 100;
const stick = document.createElement('div');
stick.classList.add('joystick');
stick.addEventListener('mousedown', handleMouseDown);
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
stick.addEventListener('touchstart', handleMouseDown);
document.addEventListener('touchmove', handleMouseMove);
document.addEventListener('touchend', handleMouseUp);
let dragStart = null;
let currentPos = { x: 0, y: 0 };
function handleMouseDown(event) {
stick.style.transition = '0s';
if (event.changedTouches) {
dragStart = {
x: event.changedTouches[0].clientX,
y: event.changedTouches[0].clientY,
};
return;
}
dragStart = {
x: event.clientX,
y: event.clientY,
};
}
function handleMouseMove(event) {
if (dragStart === null) return;
event.preventDefault();
if (event.changedTouches) {
event.clientX = event.changedTouches[0].clientX;
event.clientY = event.changedTouches[0].clientY;
}
const xDiff = event.clientX - dragStart.x;
const yDiff = event.clientY - dragStart.y;
const angle = Math.atan2(yDiff, xDiff);
const distance = Math.min(maxDiff, Math.hypot(xDiff, yDiff));
const xNew = distance * Math.cos(angle);
const yNew = distance * Math.sin(angle);
stick.style.transform = `translate3d(${xNew}px, ${yNew}px, 0px)`;
currentPos = { x: xNew, y: yNew };
}
function handleMouseUp(event) {
if (dragStart === null) return;
stick.style.transition = '.2s';
stick.style.transform = `translate3d(0px, 0px, 0px)`;
dragStart = null;
currentPos = { x: 0, y: 0 };
}
parent.appendChild(stick);
return {
getPosition: () => currentPos,
};
}