JSFiddle - React, Tailwind, and code Playground
HTML
<div class="field" id="field">
<div class="field__ball"></div>
</div>
CSS
.field {
width: 300px;
height: 200px;
border: 10px solid #000000;
background-color: greenyellow;
cursor: pointer;
}
.field__ball {
position: relative;
width: 50px;
height: 50px;
box-sizing: border-box;
background-color: red;
border-radius: 50%;
border: 2px solid gray;
transition: transform 0.1s;
}
JavaScript
function initField(field) {
const ball = field.querySelector('.field__ball'),
ballWidth = ball.offsetWidth,
ballHeight = ball.offsetHeight,
fieldWidth = field.clientWidth,
fieldHeight = field.clientHeight,
rightEdge = fieldWidth - ballWidth,
bottomEdge = fieldHeight - ballHeight;
let ignoreMove = false,
last = null;
function update(e) {
const box = field.getBoundingClientRect();
let fieldX = e.clientX - box.left - field.clientLeft - ballWidth / 2,
fieldY = e.clientY - box.top - field.clientTop - ballHeight / 2;
if (fieldX < 0) fieldX = 0;
if (fieldX > rightEdge) fieldX = rightEdge;
if (fieldY < 0) fieldY = 0;
if (fieldY > bottomEdge) fieldY = bottomEdge;
ball.style.transform = `translate(${fieldX}px, ${fieldY}px`;
}
function track(e) {
if (ignoreMove) {
last = e;
return;
}
update(e);
ignoreMove = true;
setTimeout(function self() {
if (last) {
update(last);
setTimeout(self, 200);
last = null;
}
ignoreMove = false;
}, 200);
}
field.addEventListener('mousedown', e => {
update(e);
field.addEventListener('mousemove', track);
});
field.addEventListener('mouseup', e => {
update(e);
field.removeEventListener('mousemove', track);
})
}
initField(document.getElementById('field'));