必须设置navigationStyle: "custom",否则原生导航栏独立渲染,onPageScroll无法控制其显隐;需手动处理状态栏高度、胶囊按钮预留空间,并用fixed定位+onPageScroll判断滚动方向实现滑动显隐。

navigationStyle: "custom" 是前提,否则滑动逻辑无效
页面没关掉原生导航栏,onPageScroll 监听再准也没用——原生导航栏是独立渲染层,滚动事件根本触碰不到它的显隐控制。必须先在 pages.json 对应页面的 style 里设 "navigationStyle": "custom",让 uni-app 把顶部区域完全交给你管。
常见错误是只加了 "navigationBarHidden": true 或 "titleNView": false,结果小程序里导航栏消失但状态栏压字,APP 端则完全没反应。这两个配置和滑动显隐无关,别混用。
-
navigationStyle: "custom"后,所有原生导航行为(返回、标题、胶囊按钮)全部失效,你得自己画顶部区域 - 必须手动处理状态栏高度:用
uni.getSystemInfoSync().statusBarHeight拿值,或更稳妥地用 CSS 变量env(safe-area-inset-top) - 如果页面本身是 tabbar 页面,还要额外处理
uni.hideTabBar()的时机,避免 tabBar 和自定义导航栏打架
onPageScroll 里判断滚动方向和阈值,不是监听 scroll-y
很多人误以为要给 <scroll-view> 加 @scroll,其实这是错的。页面级滚动显隐必须用 onPageScroll 生命周期钩子,它才能捕获整个页面的滚动偏移(e.scrollTop),而 scroll-view 的 @scroll 只作用于组件内部,无法反映页面整体行为。
关键不是“有没有滚动”,而是“往哪滚”和“滚了多少”。你需要存上一次的 scrollTop,和当前对比:
- 向下滚动且差值 > 10px → 触发隐藏(导航栏
transform: translateY(-100%)) - 向上滚动且差值 > 10px → 触发显示(恢复
transform: translateY(0)) - 阈值设太小(如 1px)会导致抖动;设太大(如 50px)会延迟响应
注意:H5 和小程序中 onPageScroll 触发频率不同,iOS 尤其节流严重,别在回调里直接改 DOM,用 requestAnimationFrame 做平滑过渡。
自定义导航栏必须用 fixed 定位 + z-index 控制层级
用 position: fixed 是硬性要求。relative 或 absolute 在某些安卓机上会随页面滚动“漂移”,尤其当页面有 transform 或 scale 时,fixed 才能真正钉住顶部。
z-index 得够高,但不能无脑设 999。推荐值:99(覆盖所有内容)、-1(被系统状态栏盖住)都是坑。稳妥写法是:
-
top: 0+left: 0+right: 0 -
z-index: 990(避开 uni-app 内部弹窗 z-index 范围) - 加上
will-change: transform提升渲染性能,减少 iOS 卡顿
别用 margin-top: -44px 把内容往上顶来“假装”隐藏——这会导致点击区域错位、下拉刷新触发区偏移,真机调试时问题集中爆发。
胶囊按钮(微信右上角「…」)无法隐藏,必须预留空间
即使开了 navigationStyle: "custom",微信小程序仍强制渲染右侧胶囊按钮,CSS 的 display: none、opacity: 0、pointer-events: none 全部无效,DOM 操作也会被拦截。
唯一可行方案是承认它的存在,并适配布局:
- 自定义导航栏高度固定为
44px(不含状态栏) - 右侧留出至少
80px宽度(胶囊按钮宽度 + 间距),用透明占位或图标对齐 - 不要试图用负 margin 盖住它,真机会出现点击穿透或闪现
这个限制是微信平台级的,不是 uni-app bug。如果你的 UI 必须彻底隐藏它,只能放弃小程序端,或改用 transparentTitle: "always"(仅 APP 支持)+ 透明背景叠加,但 H5 和小程序依然不行。
最易忽略的点是:滑动显隐逻辑必须和页面生命周期对齐。比如从详情页返回列表页时,如果列表页的 onPageScroll 没及时重置状态,可能出现“刚进页面导航栏就消失”的诡异情况。建议在 onShow 里重置显隐状态变量,而不是只依赖滚动事件单向驱动。


















