滚动网站元素进入视口时requestAnimationFrame未触发问题
问题分析与修复
核心问题
你的代码里start_time是页面一加载就赋值的,等你滚动到元素位置时,time = 当前时间 - start_time的结果早就超过动画的duration了,所以run函数里的time <= duration判断直接不成立,动画根本跑不起来。另外滚动事件会频繁触发,每次满足条件都调用requestAnimationFrame(run)还会导致动画重复启动,得加个锁。
修复后的代码
// 新增动画状态锁,防止重复触发 let isAnimating = false; // 把start_time改成在触发动画时再初始化 let start_time; window.addEventListener('scroll', ()=>{ const contentPosition = element.getBoundingClientRect().top; const screenPosition = window.innerHeight; // 只有元素进入视口且未在动画中时才触发 if(contentPosition < screenPosition && !isAnimating){ isAnimating = true; start_time = new Date().getTime(); requestAnimationFrame(run); } }); function run(){ const time = new Date().getTime() - start_time; const result = easeInOutQuart(time, from, to - from, duration); element.style.left = result+"px"; if(time <= duration){ requestAnimationFrame(run); } else { // 动画结束后解锁,若只需触发一次可删除此行 isAnimating = false; } } function easeInOutQuart(t, b, c, d){ if((t /= d / 2) < 1){ return c / 2 * t * t * t * t + b; } else { return -c / 2 * ((t -= 2) * t * t * t - 2) + b; } }
关键改动说明
- 新增
isAnimating变量:标记当前是否在执行动画,避免滚动事件频繁触发导致动画重复启动。 - 延迟初始化
start_time:只有当元素进入视口且要启动动画时,才把当前时间赋值给start_time,确保动画计算的时间是从触发那一刻开始的。 - 动画结束后解锁:如果需要元素再次进入视口时重复执行动画,就保留
isAnimating = false;如果只需要执行一次,直接删掉这行即可。
内容的提问来源于stack exchange,提问作者Ruudje
相关产品推荐
相关产品推荐

