You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现类似fullpage.js的锚点吸附滚动效果?

手动实现滚动吸附到锚点的方案

嘿,我完全懂你的需求——不想依赖fullpage.js,自己手动实现右侧滚动栏的锚点吸附效果,滚动时不能停在两个锚点之间,向上滚自动吸到锚点1,向下滚吸到锚点2,和平滑跳转的体验一致对吧?这事儿不难,我给你一套适配你现有代码的实现方案:

核心思路

  1. 监听右侧滚动容器的滚动事件,用防抖函数避免滚动过程中频繁触发逻辑(不然会出现反复跳动)
  2. 滚动停止后,计算当前滚动位置与两个锚点的距离
  3. 根据位置远近或滚动方向,自动触发平滑滚动到对应的锚点
  4. 确保和你现有的平滑滚动逻辑兼容

代码实现

第一步:调整你的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:01:18