如何实现类似fullpage.js的锚点吸附滚动效果?
手动实现滚动吸附到锚点的方案
嘿,我完全懂你的需求——不想依赖fullpage.js,自己手动实现右侧滚动栏的锚点吸附效果,滚动时不能停在两个锚点之间,向上滚自动吸到锚点1,向下滚吸到锚点2,和平滑跳转的体验一致对吧?这事儿不难,我给你一套适配你现有代码的实现方案:
核心思路
- 监听右侧滚动容器的滚动事件,用防抖函数避免滚动过程中频繁触发逻辑(不然会出现反复跳动)
- 滚动停止后,计算当前滚动位置与两个锚点的距离
- 根据位置远近或滚动方向,自动触发平滑滚动到对应的锚点
- 确保和你现有的平滑滚动逻辑兼容
代码实现
第一步:调整你的HTML结构
先给滚动容器加个ID,方便我们监听滚动事件,同时明确锚点与区块的对应关系:
<!-- 左栏固定部分(假设你已有固定布局的CSS) --> <div class="left-column" style="position: fixed; left:0; top:0; height:100vh; width: 20%;"> <!-- 左栏内容 --> </div> <!-- 右栏滚动部分 --> <div class="right-column" style="margin-left:20%;"> <div class="scroller" id="right-scroller"> <a name="bespoke" id="bespoke"></a> <div class="v-image-1" style="height:100vh;"> <div class="row no-margin"> <div class="col-md-offset-1 col-md-10 no-padding">BESPOKE</div> <div class="col-md-1 no-padding"> <a href="#targeted"><img src="images/but-scroll.png" alt="scroll" /></a> </div> </div> </div> <a name="targeted" id="targeted"></a> <div class="v-image-2" style="height:100vh;"> <div class="row no-margin"> <div class="col-md-offset-1 col-md-10 no-padding">TARGETED</div> <div class="col-md-1 no-padding"> <a href="#bespoke"><img src="images/but-scroll.png" alt="scroll" /></a> </div> </div> </div> </div> </div>
第二步:添加CSS确保滚动容器正常工作
给滚动容器设置合适的高度和滚动属性:
#right-scroller { height: 100vh; overflow-y: auto; scroll-behavior: smooth; /* 可替代原有平滑滚动JS,也可保留你的逻辑 */ } /* 可选:隐藏滚动条,优化移动端视觉体验 */ #right-scroller::-webkit-scrollbar { display: none; }
第三步:编写JS实现吸附逻辑
结合你现有的平滑滚动代码,添加防抖和吸附判断:
// 防抖函数:避免滚动过程中频繁触发逻辑 function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } // 获取锚点与滚动容器元素 const bespokeAnchor = document.getElementById('bespoke'); const targetedAnchor = document.getElementById('targeted'); const scroller = document.getElementById('right-scroller'); // 计算锚点相对于滚动容器的位置 const bespokeTop = bespokeAnchor.offsetTop; const targetedTop = targetedAnchor.offsetTop; // 两个锚点的中间位置,作为吸附判断阈值 const middlePoint = (bespokeTop + targetedTop) / 2; // 滚动监听逻辑 const handleScroll = debounce(() => { const currentScrollTop = scroller.scrollTop; // 判断是否处于两个锚点之间,触发吸附 if (currentScrollTop > bespokeTop && currentScrollTop < targetedTop) { if (currentScrollTop < middlePoint) { // 靠近锚点1,向上吸附 scroller.scrollTo({ top: bespokeTop, behavior: 'smooth' }); } else { // 靠近锚点2,向下吸附 scroller.scrollTo({ top: targetedTop, behavior: 'smooth' }); } } }, 200); // 防抖时间可根据体验调整,建议150-250ms // 绑定滚动事件 scroller.addEventListener('scroll', handleScroll); // 保留你现有的点击平滑跳转逻辑(若需要) document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function (e) { e.preventDefault(); const targetId = this.getAttribute('href').substring(1); const targetAnchor = document.getElementById(targetId); if (targetAnchor) { scroller.scrollTo({ top: targetAnchor.offsetTop, behavior: 'smooth' }); } }); });
关键细节说明
- 防抖函数:设置延迟是为了等用户停止滚动后再触发吸附,避免滚动过程中出现频繁跳动
- 阈值判断:用锚点中间位置作为判断依据,也可以改成根据滚动方向(记录上一次滚动位置,判断上下方向)来吸附
- 移动端适配:代码支持触摸滚动,可根据移动端屏幕尺寸调整滚动容器高度,比如用
calc(100vh - 导航栏高度)适配顶部导航 - 兼容性:
scrollTo的behavior: 'smooth'在现代浏览器都支持,若需兼容旧浏览器,可替换成你原来的jQuery animate平滑滚动逻辑
这样实现后,用户滚动右侧栏时就不会停在两个锚点之间,会自动平滑吸附到最近的锚点,可控性极强,方便你根据移动端需求调整细节~
内容的提问来源于stack exchange,提问作者Damien
相关产品推荐
相关产品推荐

