求助:使用setInterval实现div宽度往复变化的原生JS方案
解决方案
一、使用setInterval的实现方案
先解决你之前的问题核心:错误通常源于步长计算偏差、状态切换逻辑疏漏,或是意外触发clearInterval。以下是精准实现的代码:
const line = document.querySelector('.line'); let currentWidth = 0; let isExpanding = true; const frameGap = 16; // 对应约60帧/秒的间隔 const singlePhaseDuration = 3000; // 单方向动画时长3秒 const step = 100 / (singlePhaseDuration / frameGap); // 每帧宽度变化量 const animationTimer = setInterval(() => { if (isExpanding) { currentWidth += step; // 到达100%时修正值并切换收缩状态 if (currentWidth >= 100) { currentWidth = 100; isExpanding = false; } } else { currentWidth -= step; // 到达0时修正值并切换展开状态 if (currentWidth <= 0) { currentWidth = 0; isExpanding = true; } } line.style.width = `${currentWidth}%`; }, frameGap); // 停止动画时调用:clearInterval(animationTimer)
关键说明
- 用
isExpanding状态变量精准切换展开/收缩阶段 - 基于帧率计算每帧步长,避免动画跳变或提前终止
- 到达边界值时强制修正宽度,防止后续计算出现异常
二、更优方案:使用requestAnimationFrame
虽然你指定用setInterval,但requestAnimationFrame是浏览器专为动画设计的API,性能更优、动画更流畅,且不会出现定时器时间偏移问题,推荐优先使用:
const line = document.querySelector('.line'); let startTime = null; const fullCycleDuration = 6000; // 一个完整循环(展开+收缩)时长6秒 function animate(timestamp) { if (!startTime) startTime = timestamp; const elapsedTime = timestamp - startTime; // 计算当前循环进度:0-1对应一次完整的展开+收缩 const progress = (elapsedTime % fullCycleDuration) / fullCycleDuration; let currentWidth; if (progress <= 0.5) { // 前3秒:从0到100% currentWidth = progress * 2 * 100; } else { // 后3秒:从100%到0 currentWidth = (1 - (progress - 0.5) * 2) * 100; } line.style.width = `${currentWidth}%`; requestAnimationFrame(animate); } // 启动动画 requestAnimationFrame(animate); // 停止动画时调用(需保存请求ID): // const animationId = requestAnimationFrame(animate); // cancelAnimationFrame(animationId);
关键说明
- 基于时间戳计算进度,动画精度不受主线程阻塞影响
- 通过取模运算天然实现无限循环,无需额外状态变量
- 逻辑更简洁,避免定时器可能出现的累积误差
常见问题排查
你之前遇到的提前清除setInterval或功能失效,大概率是以下原因:
- 定时器间隔设置不合理,导致步长计算错误,提前触发边界条件
- 状态切换时未修正当前宽度值,导致下一阶段计算逻辑异常
- 代码作用域问题导致定时器变量被意外覆盖,触发
clearInterval
内容的提问来源于stack exchange,提问作者yanballas
相关产品推荐
相关产品推荐

