You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现Div持续自动移动?DOM无法正常更新问题求助

问题描述

需要实现页面中多个Div元素持续随机移动,直到点击按钮停止。尝试用while(true)循环实现时,DOM无法按预期更新;目前仅点击按钮时能触发Div移动(此时DOM会刷新),不知道怎么实现自动持续移动。

页面代码

<div className="bg-[#151A1D] place-items-center grid grid-cols-5 grid-rows-3 gap-3 h-screen w-screen">
  <>
    <div id="box0" className={`transition-all duration-700 absolute bg-[#F8F3F4] w-[300px] h-[300px]`} style={{ top: toparrnew[0], left: leftarrnew[0] }} >
      <Canvas className="canvas">
        <OrbitControls enableZoom={true} />
        <ambientLight intensity={0.5} />
        <directionalLight position={[-2, 5, 2]} intensity={1} />
        <Boxy />
      </Canvas>
    </div>
    <div id="box1" className={`transition-all duration-700 absolute bg-[#F8F3F4] w-[300px] h-[300px]`} style={{ top: toparrnew[1], left: leftarrnew[1] }} >
      <Canvas className="canvas">
        <OrbitControls enableZoom={true} />
        <ambientLight intensity={0.5} />
        <directionalLight position={[-2, 5, 2]} intensity={1} />
        <Boxy />
      </Canvas>
    </div>
    <div id="box2" className={`transition-all duration-700 absolute bg-[#F8F3F4] w-[300px] h-[300px]`} style={{ top: toparrnew[2], left: leftarrnew[2] }} >
      <Canvas className="canvas">
        <OrbitControls enableZoom={true} />
        <ambientLight intensity={0.5} />
        <directionalLight position={[-2, 5, 2]} intensity={1} />
        <Boxy />
      </Canvas>
    </div>
    <div id="box3" className={`transition-all duration-700 absolute bg-[#F8F3F4] w-[300px] h-[300px]`} style={{ top: toparrnew[3], left: leftarrnew[3] }} >
      <Canvas className="canvas">
        <OrbitControls enableZoom={true} />
        <ambientLight intensity={0.5} />
        <directionalLight position={[-2, 5, 2]} intensity={1} />
        <Boxy />
      </Canvas>
    </div>
    <div id="box4" className={`transition-all duration-700 absolute bg-[#F8F3F4] w-[300px] h-[300px]`} style={{ top: toparrnew[4], left: leftarrnew[4] }} >
      <Canvas className="canvas">
        <OrbitControls enableZoom={true} />
        <ambientLight intensity={0.5} />
        <directionalLight position={[-2, 5, 2]} intensity={1} />
        <Boxy />
      </Canvas>
    </div>
    <div id="box5" className={`transition-all duration-700 absolute bg-[#F8F3F4] w-[300px] h-[300px]`} style={{ top: toparrnew[5], left: leftarrnew[5] }} >
      <Canvas className="canvas">
        <OrbitControls enableZoom={true} />
        <ambientLight intensity={0.5} />
        <directionalLight position={[-2, 5, 2]} intensity={1} />
        <Boxy />
      </Canvas>
    </div>
    <div id="box6" className={`transition-all duration-700 absolute bg-[#F8F3F4] w-[300px] h-[300px]`} style={{ top: toparrnew[6], left: leftarrnew[6] }} >
      <Canvas className="canvas">
        <OrbitControls enableZoom={true} />
        <ambientLight intensity={0.5} />
        <directionalLight position={[-2, 5, 2]} intensity={1} />
        <Boxy />
      </Canvas>
    </div>
    <div id="box7" className={`transition-all duration-700 absolute bg-[#F8F3F4] w-[300px] h-[300px]`} style={{ top: toparrnew[7], left: leftarrnew[7] }} >
      <Canvas className="canvas">
        <OrbitControls enableZoom={true} />
        <ambientLight intensity={0.5} />
        <directionalLight position={[-2, 5, 2]} intensity={1} />
        <Boxy />
      </Canvas>
    </div>
    <div id="box8" className={`transition-all duration-700 absolute bg-[#F8F3F4] w-[300px] h-[300px]`} style={{ top: toparrnew[8], left: leftarrnew[8] }} >
      <Canvas className="canvas">
        <OrbitControls enableZoom={true} />
        <ambientLight intensity={0.5} />
        <directionalLight position={[-2, 5, 2]} intensity={1} />
        <Boxy />
      </Canvas>
    </div>
    <div id="box9" className={`transition-all duration-700 absolute bg-[#F8F3F4] w-[300px] h-[300px]`} style={{ top: toparrnew[9], left: leftarrnew[9] }} >
      <Canvas className="canvas">
        <OrbitControls enableZoom={true} />
        <ambientLight intensity={5} />
        <directionalLight position={[-2, 5, 2]} intensity={1} />
        <Boxy />
      </Canvas>
    </div>
    <div className="col-start-1 col-span-5 row-start-2">
      <Button title="PLAY" onPress={() => { controles() }}></Button>
    </div>
    <button id="clicky" className="display:none" onClick={() => {console.log("aa")}}></button>
  </>
</div>

调用函数代码

const controlHelper = async () => {
  findRandom();
}

const controles = async () => {
  thisFunc()
  await sleep(1000);
  setFloatingTime(false);
  await sleep(1000);
  controlHelper();
}

解决方案

核心问题原因

浏览器JS是单线程模型,while(true)会直接阻塞事件循环,导致DOM渲染任务被无限延后,页面自然不会更新。必须用异步周期性任务来实现持续移动。

实现方案

1. 用setInterval实现周期性移动

通过React状态保存定时器ID,实现启动/停止的切换逻辑:

import { useState } from 'react';

// 简化后的组件示例
function MovingBoxes() {
  const [toparrnew, setToparrnew] = useState(Array(10).fill('0px'));
  const [leftarrnew, setLeftarrnew] = useState(Array(10).fill('0px'));
  const [moveTimer, setMoveTimer] = useState(null);

  // 生成随机位置
  const findRandom = () => {
    const newTop = toparrnew.map(() => `${Math.random() * (window.innerHeight - 300)}px`);
    const newLeft = leftarrnew.map(() => `${Math.random() * (window.innerWidth - 300)}px`);
    setToparrnew(newTop);
    setLeftarrnew(newLeft);
  };

  // 切换移动状态
  const toggleMovement = () => {
    if (moveTimer) {
      // 停止移动:清除定时器
      clearInterval(moveTimer);
      setMoveTimer(null);
    } else {
      // 启动移动:立即执行一次,之后每隔1000ms更新一次位置
      findRandom();
      const timerId = setInterval(findRandom, 1000);
      setMoveTimer(timerId);
    }
  };

  return (
    <div className="bg-[#151A1D] place-items-center grid grid-cols-5 grid-rows-3 gap-3 h-screen w-screen">
      {/* 用map循环渲染所有Box,减少冗余代码 */}
      {Array(10).fill().map((_, index) => (
        <div 
          key={`box-${index}`}
          id={`box${index}`} 
          className={`transition-all duration-700 absolute bg-[#F8F3F4] w-[300px] h-[300px]`} 
          style={{ top: toparrnew[index], left: leftarrnew[index] }}
        >
          <Canvas className="canvas">
            <OrbitControls enableZoom={true} />
            <ambientLight intensity={index === 9 ? 5 : 0.5} />
            <directionalLight position={[-2, 5, 2]} intensity={1} />
            <Boxy />
          </Canvas>
        </div>
      ))}
      <div className="col-start-1 col-span-5 row-start-2">
        <Button title={moveTimer ? 'STOP' : 'PLAY'} onPress={toggleMovement}></Button>
      </div>
    </div>
  );
}

2. 更流畅的动画方案(可选)

如果需要更平滑的移动效果,可以用requestAnimationFrame替代setInterval,它会和浏览器渲染帧同步:

let animationId;

const startSmoothMove = () => {
  const updatePosition = () => {
    findRandom();
    animationId = requestAnimationFrame(updatePosition);
  };
  updatePosition();
};

const stopSmoothMove = () => {
  cancelAnimationFrame(animationId);
};

关键注意事项

  • 定时器间隔要和Div的transition-duration匹配(比如动画时长700ms,间隔设为1000ms),避免动画未完成就更新位置导致卡顿。
  • 必须用状态保存定时器ID,确保组件卸载或停止时能正确清除定时器,避免内存泄漏。
  • 用map循环渲染重复组件,大幅减少代码冗余,提升可维护性。

内容的提问来源于stack exchange,提问作者AlbaraaoTHMAN

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 21:48:07