我一直在尝试创建弹跳球动画。
我遇到了一个特殊的问题。我已经设法让球弹起但是,由于某种原因,它似乎只会向右滚动,直到它与视口的右侧碰撞。
(要移动球,请用鼠标按钮单击它,然后向任意方向快速拖动它,然后释放鼠标按钮)
我想了解可能导致这种意外行为的原因以及如何纠正它。
这是我的 JavaScript 文件的片段:
var ball = document.querySelector('.ball'),
isDragging = false,
isAnimating = false,
isAnimationAllowed = true, // New flag to control animation while dragging
offsetX,
offsetY,
velocityX = 0,
velocityY = 0,
friction = 0.95,
gravity = 0.5,
floorLevel = 500,
animationFrame;
ball.addEventListener('mousedown', startDragging);
ball.addEventListener('touchstart', startDragging);
function startDragging(event) {
isDragging = true;
isAnimating = false; // Reset isAnimating to false
isAnimationAllowed = false; // Disable animation while dragging
offsetX = event.clientX - ball.getBoundingClientRect().left;
offsetY = event.clientY - ball.getBoundingClientRect().top;
document.addEventListener('mousemove', drag);
document.addEventListener('mouseup', stopDragging);
document.addEventListener('touchmove', drag);
document.addEventListener('touchend', stopDragging);
}
function drag(event) {
if (!isDragging) return;
event.preventDefault();
var newX, newY;
if (event.type === 'mousemove') {
newX = event.clientX - offsetX;
newY = event.clientY - offsetY;
} else if (event.type === 'touchmove') {
newX = event.touches[0].clientX - offsetX;
newY = event.touches[0].clientY - offsetY;
}
ball.style.left = newX + "px";
ball.style.top = newY + "px";
velocityX = newX - ball.offsetLeft;
velocityY = newY - ball.offsetTop;
}
function stopDragging() {
isDragging = false;
isAnimationAllowed = true; // Enable animation after dragging stops
document.removeEventListener('mousemove', drag);
document.removeEventListener('mouseup', stopDragging);
document.removeEventListener('touchmove', drag);
document.removeEventListener('touchend', stopDragging);
if (!isAnimating) {
animate();
}
}
function animate() {
if (!isAnimationAllowed) return; // Check if animation is allowed
isAnimating = true;
animationFrame = requestAnimationFrame(animate);
velocityX *= friction;
velocityY += gravity;
var newX = ball.offsetLeft + velocityX;
var newY = ball.offsetTop + velocityY;
if (newY >= floorLevel - ball.offsetHeight) {
newY = floorLevel - ball.offsetHeight;
velocityY *= -0.6; // Bounce effect
}
if (newX < 0) {
newX = 0;
velocityX *= -0.6; // Bounce effect for the left boundary
} else if (newX > window.innerWidth - ball.offsetWidth) {
newX = window.innerWidth - ball.offsetWidth;
velocityX *= -0.6; // Bounce effect for the right boundary
}
ball.style.left = newX + "px";
ball.style.top = newY + "px";
if (Math.abs(velocityX) < 0.1 && Math.abs(velocityY) < 0.1 && newY >= floorLevel - ball.offsetHeight) {
cancelAnimationFrame(animationFrame);
isAnimating = false;
}
}
div.ball {
width: 50px;
height: 50px;
border-radius: 50%;
background: radial-gradient(circle at 65% 15%, white 1px, aqua 3%, darkblue 60%, aqua 100%);
position: absolute;
}
<div class="ball"></div>
如果您在第一次调用
velocityX
时检查 velocityY
和 stopDragging()
的值,您会发现它们始终是 0
。这是因为drag(event)
中的以下两行无法正常工作:
velocityX = newX - ball.offsetLeft;
velocityY = newY - ball.offsetTop;
看来
newX
和ball.offsetLeft
具有相同的值,所以你的逻辑有问题。
所以,我将这两行替换为(除以 10 只是为了降低速度):
velocityX = newX / 10;
velocityY = newY / 10;
我们现在可以看到您所描述的效果,即在您释放球后,无论您当时是向左还是向右拖动,球总是向右滚动。
现在,如果您将
velocityX
定为负数,例如:
velocityX = - newX / 10;
然后你会发现球总是滚到左边。那么问题来了。
因此,请研究我如何通过引入变量
drag(event)
和 oldX
来更改 oldY
,以跟踪球位置的先前坐标。
您可能还想在 MDN 上阅读有关
movementX
的 MouseEvent
属性,因为这可能对您也感兴趣。
我将
floorlevel
设置为 400,以便在 Snack 代码片段中大部分时间都可以看到球。
var ball = document.querySelector('.ball'),
isDragging = false,
isAnimating = false,
isAnimationAllowed = true, // New flag to control animation while dragging
offsetX,
offsetY,
velocityX = 0,
velocityY = 0,
friction = 0.95,
gravity = 0.5,
floorLevel = 400,
animationFrame;
let newX, newY;
ball.addEventListener('mousedown', startDragging);
ball.addEventListener('touchstart', startDragging);
function startDragging(event) {
isDragging = true;
isAnimating = false; // Reset isAnimating to false
isAnimationAllowed = false; // Disable animation while dragging
offsetX = event.clientX - ball.getBoundingClientRect().left;
offsetY = event.clientY - ball.getBoundingClientRect().top;
document.addEventListener('mousemove', drag);
document.addEventListener('mouseup', stopDragging);
document.addEventListener('touchmove', drag);
document.addEventListener('touchend', stopDragging);
}
function drag(event) {
if (!isDragging) return;
event.preventDefault();
let oldX = newX;
let oldY = newY;
if (event.type === 'mousemove') {
newX = event.clientX - offsetX;
newY = event.clientY - offsetY;
} else if (event.type === 'touchmove') {
newX = event.touches[0].clientX - offsetX;
newY = event.touches[0].clientY - offsetY;
}
let directionX = Math.sign(newX - oldX);
let directionY = Math.sign(newY - oldY);
ball.style.left = newX + "px";
ball.style.top = newY + "px";
velocityX = directionX * newX / 10;
velocityY = directionY * newY / 10;
}
function stopDragging() {
isDragging = false;
isAnimationAllowed = true; // Enable animation after dragging stops
document.removeEventListener('mousemove', drag);
document.removeEventListener('mouseup', stopDragging);
document.removeEventListener('touchmove', drag);
document.removeEventListener('touchend', stopDragging);
if (!isAnimating) {
animate();
}
}
function animate() {
if (!isAnimationAllowed) return; // Check if animation is allowed
isAnimating = true;
animationFrame = requestAnimationFrame(animate);
velocityX *= friction;
velocityY += gravity;
var newX = ball.offsetLeft + velocityX;
var newY = ball.offsetTop + velocityY;
if (newY >= floorLevel - ball.offsetHeight) {
newY = floorLevel - ball.offsetHeight;
velocityY *= -0.6; // Bounce effect
}
if (newX < 0) {
newX = 0;
velocityX *= -0.6; // Bounce effect for the left boundary
} else if (newX > window.innerWidth - ball.offsetWidth) {
newX = window.innerWidth - ball.offsetWidth;
velocityX *= -0.6; // Bounce effect for the right boundary
}
ball.style.left = newX + "px";
ball.style.top = newY + "px";
if (Math.abs(velocityX) < 0.1 && Math.abs(velocityY) < 0.1 && newY >= floorLevel - ball.offsetHeight) {
cancelAnimationFrame(animationFrame);
isAnimating = false;
}
}
div.ball {
width: 50px;
height: 50px;
border-radius: 50%;
background: radial-gradient(circle at 65% 15%, white 1px, aqua 3%, darkblue 60%, aqua 100%);
position: absolute;
}
<div class="ball"></div>