js运动函数定时器没办法清除

<body>
  <button id="begin">开始运动button>
  <button id="stop">停止运动button>
  <div>div>
  <script>
    let begin = document.querySelector('#begin');
    let cease = document.querySelector('#stop');
    let div = document.querySelector('div');
    /* let target = 600;
    let direction = "top" */
    let timer;
    
    begin.onclick = function () {
      animate(div, {left:0,top:0});
    }

    function animate(el, obj) {
      
      timer = setInterval(() => {
        let count =0;
        for (key in obj) {
          let target = obj[key];
          let current = parseInt(getComputedStyle(el)[key]);
          console.log(obj.current);

          let speed = (target - current) / 10;
          console.log(target,current);
          if (speed < 0) {
            speed = Math.floor(speed);
          } else {
            speed = Math.ceil(speed);
          } 
          el.style[key] = current + speed + "px";
          //当前的值等于目标值清除定时器
          if (speed == 0) {
              clearInterval(timer);
            }
        }
      }, 200);
    }
    cease.onclick = function () {
      clearInterval(timer);
    }


  script>


![img](https://img-mid.csdnimg.cn/release/static/image/mid/ask/806616990086139.jpg "#left")

原来位置left: 50px; top: 400px;
js目标移动到left:0,top:0
执行完left后定时器就停止了,top还没走到0

img

基于Monster 组和GPT的调写:
代码中有一个问题是在 setInterval 中使用了一个 for...in 循环遍历对象的属性,而在循环中使用了一个变量 key,但在 console.log(obj.current) 中又将它写成了 obj.current,这样会导致输出 undefined。正确的写法应该是 console.log(current)。

另一个问题是,当多个属性的动画同时进行时,只要其中一个属性的动画到达目标值就会清除定时器,这会导致其他属性的动画还没有完成就被中止了。

为了解决这个问题,要在每次循环中记录当前所有属性的动画是否都已完成,只有所有属性的动画都完成了,才清除定时器。

以下是修改后的代码



<body>
  <button id="begin">开始运动</button>
  <button id="stop">停止运动</button>
  <div style="position: absolute; left: 50px; top: 400px;">Hello, world!</div>
  <script>
    let begin = document.querySelector('#begin');
    let cease = document.querySelector('#stop');
    let div = document.querySelector('div');
    let timer;

    begin.onclick = function () {
      animate(div, {left: 0, top: 0});
    }

    function animate(el, obj) {
      clearInterval(timer);
      timer = setInterval(() => {
        let allFinished = true;
        for (let key in obj) {
          let target = obj[key];
          let current = parseInt(getComputedStyle(el)[key]);
          let speed = (target - current) / 10;
          if (speed < 0) {
            speed = Math.floor(speed);
          } else {
            speed = Math.ceil(speed);
          } 
          el.style[key] = current + speed + "px";
          if (current !== target) {
            allFinished = false;
          }
        }
        if (allFinished) {
          clearInterval(timer);
        }
      }, 200);
    }

    cease.onclick = function () {
      clearInterval(timer);
    }
  </script>
</body>

回答引用ChatGPT:在你的代码中,如果一个属性已经达到目标值,则应该将该属性从对象中删除,以便定时器可以在所有属性都到达目标值时停止。你可以通过添加一个变量 finishCount 来跟踪已完成动画的属性数量,并在计算 speed 和更新属性值之前检查 finishCount 的值。

下面是更新后的代码:

<body>
  <button id="begin">开始运动</button>
  <button id="stop">停止运动</button>
  <div style="position: absolute; left: 50px; top: 400px; width: 100px; height: 100px; background-color: red;"></div>
  <script>
    let begin = document.querySelector('#begin');
    let cease = document.querySelector('#stop');
    let div = document.querySelector('div');
    let timer;

    begin.onclick = function () {
      animate(div, {left: 0, top: 0});
    }

    function animate(el, obj) {
      let finishCount = 0;
      timer = setInterval(() => {
        for (let key in obj) {
          let target = obj[key];
          let current = parseInt(getComputedStyle(el)[key]);
          let speed = (target - current) / 10;
          if (speed < 0) {
            speed = Math.floor(speed);
          } else {
            speed = Math.ceil(speed);
          }
          let newValue = current + speed;
          if (newValue === target) {
            finishCount++;
            delete obj[key];
          }
          el.style[key] = newValue + "px";
        }
        if (finishCount === Object.keys(obj).length) {
          clearInterval(timer);
        }
      }, 200);
    }

    cease.onclick = function () {
      clearInterval(timer);
    }
  </script>
</body>


在这个修改后的代码中,我们首先在 animate 函数内部添加了一个 finishCount 变量,它的初始值为 0。然后,我们遍历 obj 中的每个属性,计算它们的 speed,更新属性值并检查是否达到目标值。如果达到目标值,则我们增加 finishCount 的值,并使用 delete 运算符将该属性从 obj 中删除。最后,我们检查 finishCount 的值是否等于 obj 中剩余属性的数量。如果是,则说明所有属性都已到达目标值,我们清除定时器。

该回答引用ChatGPT
问题分析:
根据代码,我们可以看到,当点击开始运动按钮时,会调用animate函数,该函数使用setInterval定时器来实现元素的运动。当运动到目标位置时,会清除定时器,停止运动。而当点击停止运动按钮时,会调用clearInterval函数来清除定时器,停止元素的运动。
但是,根据问题描述和截图,我们可以看到,当元素运动到目标位置的left值时,定时器被清除了,但是top值还没有到达目标位置,导致元素停在了中间位置。
问题解决:
为了解决这个问题,我们需要在清除定时器之前,判断所有属性是否都已经到达目标位置。只有当所有属性都到达目标位置时,才能清除定时器,停止元素的运动。
修改代码如下:

function animate(el, obj) {
timer = setInterval(() => {
let count = 0;
for (key in obj) {
let target = obj[key];
let current = parseInt(getComputedStyle(el)[key]);
let speed = (target - current) / 10;
if (speed < 0) {
speed = Math.floor(speed);
} else {
speed = Math.ceil(speed);
}
el.style[key] = current + speed + "px";
// 判断所有属性是否都已经到达目标位置
if (current + speed == target) {
count++;
}
}
// 所有属性都到达目标位置时,清除定时器
if (count == Object.keys(obj).length) {
clearInterval(timer);
}
}, 200);
}

修改后的代码中,我们增加了一个计数器count,用来记录已经到达目标位置的属性个数。在每次运动时,如果当前属性已经到达目标位置,则将计数器加1。当所有属性都到达目标位置时,计数器的值等于属性个数,此时清除定时器,停止元素的运动。
这样,就可以解决元素只运动到一半就停止的问题了。