为什么使用 setInterval 在 HTML 和 JavaScript 中落下方框没有按预期工作?

Why falling box in HTML & JavaScript using setInterval doesn't work as expected?

https://codepen.io/skinaqua123/pen/WNZZgEy

<div id="container">
  <div id="box"></div>
</div>

#container {
  background-color: black;
  width: 800px;
  height: 500px;
  position: relative;
}

#box {
  width: 50px;
  height: 50px;
  background-color: yellow;
  position: absolute;
  top: 0px;
  transition: top 0.5s ease-out 0s;
}

html {
  height: 100%;
  max-height: 100%;
}

const box = document.querySelector("#box");
console.log(box.offsetTop);
const fallingMovement = setInterval(() => {
  box.style.top = box.offsetTop + 50 + "px";
  if (box.offsetTop >= 500 - 50 - 50) {
    clearInterval(fallingMovement);
  }
}, 1000);

嗨,我正在为我的 html 游戏测试一些代码。我想要一个盒子掉下来但不要超出包装 div.

我的包裹高度div(容器)是500px,我的盒子是50px。我认为它应该在顶部为 450px (500-50) 时停止。

但实际上,它仍然比应有的多了 50 像素。当我将它更改为 400 时,它工作正常。

为什么会这样?即使我调用了 clearInterval,我给 setInterval 的函数是否会再次执行?

谢谢

您必须在调用 clearInterval

后更改 box.style.top
const box = document.querySelector("#box");
const fallingMovement = setInterval(() => {
  console.log('run interval --------')
  console.log('current offsetTop', box.offsetTop)
  console.log('future offsetTop', box.offsetTop + 50)

  if (box.offsetTop >= 500 - 50) {
    clearInterval(fallingMovement);
    return;
  }
  
  box.style.top = box.offsetTop + 50 + "px";
}, 1000);

间隔回调执行和渲染结果之间存在时间延迟。您可以通过比较 box.offsetTop 在更改 box.style.top 后立即和超时后的结果来看到这一点:

const fallingMovement = setInterval(() => {
  box.style.top = box.offsetTop + 50 + "px";
  if (box.offsetTop >= 500 - 50 - 50) {
    clearInterval(fallingMovement);
    console.log(box.offsetTop); // Will print 400.
    setTimeout(() => {console.log(box.offsetTop);}, 500); // Will print 450.
  }
}, 1000);

要解决此问题,您只需在更新之前执行检查,而不是更新之后:

const fallingMovement = setInterval(() => {
  if (box.offsetTop >= 500 - 50 - 50) {
    clearInterval(fallingMovement);
    return;
  }

  box.style.top = box.offsetTop + 50 + "px";
}, 1000);

或者,您可以考虑这个延迟的偏移量更新:

const fallingMovement = setInterval(() => {
  box.style.top = box.offsetTop + 50 + "px";
  if (box.offsetTop + 50 >= 500 - 50 -50) {
    clearInterval(fallingMovement);
  }
}, 1000);

其实是个很简单但是容易没意识到的问题

您的测试发生在移动调用之后。您首先调用移动,然后再测试它是否应该移动。

您可以通过稍微改变您的逻辑来修复它。首先你检查它是否应该移动,然后你调用移动。例如:

const fallingMovement = setInterval(() => {
  if (box.offsetTop < 500 - 50) {
    box.style.top = box.offsetTop + 50 + "px";
  } else {
    clearInterval(fallingMovement);
  }
}, 1000);

所以,首先我要检查盒子顶部和包装器顶部的距离是否小于 450,然后我会移动。如果大于等于450,则清空区间,不调用运动。这样,它将按预期工作。