如何实现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
相关产品推荐
相关产品推荐

