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

如何实现随页面滚动基于vh触发样式变化的菜单栏?

基于vh滚动位置切换菜单栏样式实现方案

这个效果不需要引入第三方动画库,原生浏览器API配合几十行代码就能稳定实现,性能比直接监听scroll事件计算位置更好。

核心逻辑

  • 提前定义不同滚动区间对应的菜单栏样式状态,比如:
    • 0-100vh(首屏范围):菜单栏透明、无阴影、浅色文字
    • 100vh-200vh:菜单栏白色实底、投影、深色文字
    • 200vh以上:菜单栏缩小尺寸、深色实底、浅色文字
  • 用原生IntersectionObserver监听提前放置在对应vh位置的埋点元素,埋点进入/离开视口触发线时,直接切换菜单栏的类名即可,不需要高频计算滚动距离。

完整实现代码

HTML结构

<!-- 固定顶部的菜单栏 -->
<nav id="siteNav" class="nav">
  <div class="logo">站点LOGO</div>
  <div class="nav-menu">首页 | 功能 | 关于 | 联系</div>
</nav>

<!-- 状态切换埋点,放在对应vh高度位置,不需要可见 -->
<div class="scroll-point" data-target-vh="100" style="position: absolute; top: 100vh; visibility: hidden;"></div>
<div class="scroll-point" data-target-vh="200" style="position: absolute; top: 200vh; visibility: hidden;"></div>

<!-- 测试用占位内容,模拟长页面 -->
<div style="height: 400vh; padding-top: 100px;">
  <p>页面内容区域,向下滚动即可看到菜单栏样式切换效果</p>
</div>

CSS样式

/* 菜单栏基础样式,固定在页面顶层 */
.nav {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 24px 48px;
  z-index: 999;
  /* 加过渡让样式切换更顺滑,不需要额外写keyframes */
  transition: all 0.25s ease;
}

/* 首屏透明状态 */
.nav.status-transparent {
  background: transparent;
  color: #ffffff;
}

/* 滚动过100vh的浅色状态 */
.nav.status-light {
  background: #ffffff;
  color: #1a1a1a;
  box-shadow: 0 2px 16px rgba(0,0,0,0.06);
  padding: 16px 48px;
}

/* 滚动过200vh的深色缩小状态 */
.nav.status-dark {
  background: #1a1a1a;
  color: #ffffff;
  padding: 12px 48px;
}

JavaScript逻辑

const nav = document.getElementById('siteNav');
// 初始化首屏默认状态
nav.classList.add('status-transparent');

// 初始化滚动观察器
const scrollObserver = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    const triggerVh = entry.target.dataset.targetVh;
    // 先清空所有状态类,避免类名叠加导致样式混乱
    nav.classList.remove('status-transparent', 'status-light', 'status-dark');
    
    if (entry.isIntersecting) {
      // 向下滚动碰到埋点,切换到对应状态
      switch(triggerVh) {
        case '100':
          nav.classList.add('status-light');
          break;
        case '200':
          nav.classList.add('status-dark');
          break;
      }
    } else {
      // 向上滚动离开埋点,切回上一级状态
      switch(triggerVh) {
        case '100':
          nav.classList.add('status-transparent');
          break;
        case '200':
          nav.classList.add('status-light');
          break;
      }
    }
  })
}, {
  // 触发线设置在视口最顶部,滚动到顶部碰到埋点立刻触发
  rootMargin: '0px 0px -100% 0px'
})

// 注册所有埋点到观察器
document.querySelectorAll('.scroll-point').forEach(point => {
  scrollObserver.observe(point);
})

常见问题说明

  • 之前实现失败大概率是三个原因:一是直接绑定scroll事件做计算,触发频率太高逻辑容易出bug还会卡顿;二是IntersectionObserver没有设置正确的rootMargin,触发时机不对;三是切换类名时没有清理旧的状态类,导致样式冲突。
  • 如果需要新增更多样式切换节点,只需要新增一个对应top值为目标vh的埋点元素,补充对应的CSS状态类和JS判断逻辑即可,扩展非常方便。
  • 所有状态切换的动画都通过CSS的transition实现,不需要额外写复杂的keyframes动画,性能更好维护也简单。

内容的提问来源于stack exchange,提问作者Spench

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 01:21:34