uni-app 自定义导航栏吸顶唯一可靠方案是 scroll-view + @scroll 监听,动态控制 fixed 状态并加占位防上跳;需设固定高度、onReady 后获取 offsetTop、requestAnimationFrame 节流、动态读取占位高度。

在 uni-app 里给自定义导航栏加吸顶效果,不能靠 position: sticky——微信/支付宝小程序压根不支持,H5 真机滚动时也极易“掉下来”。唯一稳的路径是:用 scroll-view 包裹内容区,监听其 @scroll,动态控制导航栏的 fixed 状态,并补占位防止内容上跳。
为什么自定义导航栏不能直接用 sticky
常见错误现象:position: sticky; top: 0 在 H5 开发工具里看着正常,一真机调试就失效;微信小程序里完全没反应;iOS App 端滚动卡顿甚至错位。
- 微信、支付宝、百度等小程序平台的渲染层未实现
sticky,连 CSS 属性识别都没有 - H5 端需同时满足:父容器无
transform/overflow: hidden、元素不能是 flex 子项且align-self: stretch、top必须显式写为0(写auto或漏写即失效) - 自定义导航栏通常已设
navigationStyle: "custom",此时页面顶部存在状态栏 + 自定义标题栏,sticky的top计算会错乱,导致吸顶位置偏移或遮挡
scroll-view + @scroll 是唯一跨端可靠的方案
核心逻辑是监听滚动位置,手动切换导航栏 class,绕过平台限制。但必须满足几个硬性条件,否则事件不触发、定位错乱、滚动卡顿。
-
scroll-view必须设固定高度,例如height: calc(100vh - var(--status-bar-height) - 44px)(减去状态栏和自定义导航栏高度),否则@scroll不触发 -
offsetTop值必须在onReady或nextTick后通过uni.createSelectorQuery()获取,onMounted中 DOM 往往未渲染,取值为0 - 吸顶后内容会上跳,必须加一个占位
<view class="placeholder"></view>,高度等于自定义导航栏的 rpx 高度(建议用getComputedStyle动态读取,别硬写88rpx) - 避免在
@scroll回调里频繁调用uni.createSelectorQuery()或getComputedStyle()——它们是同步阻塞操作,会导致卡顿甚至丢帧 - 用
requestAnimationFrame节流,防止高频滚动反复触发重排
自定义导航栏吸顶后如何联动内容滚动
用户点击 Tab 或滑动内容区时,需要让 scroll-view 滚动到对应区块,同时高亮当前 Tab。这不是简单改 scrollTop,而是靠节点位置映射和 scroll-into-view 实现。
- 每个内容区块(如
<view id="section-0">)必须设唯一id,用于scroll-view的:scroll-into-view绑定 - 点击 Tab 后,先更新
currentId,再调用uni.pageScrollTo或依赖scroll-into-view的自动平滑滚动,不要手动算offsetLeft——不同端返回值不一致,且漏算padding/margin,iOS 上经常滚偏半个 Tab - 用户滑动内容区时自动高亮对应 Tab,需用
createSelectorQuery().selectViewport().scrollOffset()获取当前scrollTop,再比对各区块boundingClientRect().top,判断临界值(建议误差容限 ≥20rpx) - 若使用 uView 的
u-sticky,注意offset-top单位是 rpx,且必须传入customNavHeight,否则在自定义导航栏下吸顶位置偏移
最易被忽略的是:占位高度必须动态读取,不能硬编码;offsetTop 必须在 onReady 后获取;scroll-view 高度缺失会导致整个吸顶逻辑静默失效。这些点一旦漏掉,表现就是“看起来写了,但就是不吸顶”。


















