如何防止滚动触发的GIF序列播放递归函数被重复调用
问题原因
- 滚动事件触发频率极高,每次滚动只要检测到
gifIsPlaying为false就会重新调用playGifService(0),相当于每次符合条件就新开一条定时器执行链,多条定时器并行就会出现重复运行的问题 - 原视口检测逻辑不严谨:仅判断
.service-list的offset.top小于窗口高度,没有判断元素是否完全离开视口下方,就算元素已经被滚到视野外,定时器还会继续执行 - 没有做定时器清除机制,之前启动的定时器任务没有被销毁,新的任务又叠加运行
修复方案
第一步:新增全局变量存储定时器和运行状态
var gifTimer = null; var gifSequenceRunning = false; // 标记整个序列是否已经启动 var currentGifIndex = 0; // 保存当前播放到的索引,可配置离开视口再回来是续播还是从头播放
第二步:封装准确的视口检测函数
function isServiceListInViewport() { const $list = $('.service-list'); if (!$list.length) return false; const rect = $list[0].getBoundingClientRect(); // 只要元素有部分出现在视口内就返回true return rect.top < window.innerHeight && rect.bottom > 0; }
第三步:重写播放逻辑,增加定时器清除处理
function playGifService() { // 不在视口内就停止序列,重置状态 if (!isServiceListInViewport()) { clearTimeout(gifTimer); gifSequenceRunning = false; // 如果需要离开再回来从头播放就放开下面这行,不需要就注释保持续播逻辑 // currentGifIndex = 0; return; } gifSequenceRunning = true; // 处理上一个索引的资源隐藏,当前索引的资源显示 const prevIndex = (currentGifIndex - 1 + 4) % 4; $(`#service-item-${prevIndex} .service-item-gif`).hide(); $(`#service-item-${prevIndex} .service-item-png`).show(); $(`#service-item-${currentGifIndex} .service-item-gif`).show(); $(`#service-item-${currentGifIndex} .service-item-png`).hide(); // 索引自增 currentGifIndex = (currentGifIndex + 1) % 4; // 存储定时器ID方便后续清除 gifTimer = setTimeout(playGifService, 3000); }
第四步:修改滚动事件触发逻辑
scroller = new LocomotiveScroll(); scroller.on("scroll", function(t, e) { // 只有当元素在视口内,且序列还没启动的时候才启动,避免重复创建执行链 if (isServiceListInViewport() && !gifSequenceRunning) { playGifService(); } });
可选优化:页面初始化时检测一次
避免元素默认就在首屏视口内,没触发滚动事件导致不播放:
$(document).ready(function() { if (isServiceListInViewport() && !gifSequenceRunning) { playGifService(); } });
内容的提问来源于stack exchange,提问作者Henry Gunawan
相关产品推荐
相关产品推荐

