微信小程序吸顶触发偏晚:把元素移出 scroll-view 后阈值没跟着更新
在一个 C 端首页里,原本搜索栏放在 scroll-view 内部随内容一起滚动,后来产品要求把它固定到顶部、不参与滚动。改完布局之后,下方 Tab 的吸顶效果立刻出问题了:用户向下滚动,明显感觉 Tab 已经滚出屏幕好一截才突然吸上去,有明显的"滞后感"和位置跳变。
现象
- Tab 吸顶触发时机比预期晚约 60px(恰好是搜索栏的高度)
- 滚动回来时又正常了,但向下再滚一次依然偏晚
- 代码逻辑没动,仅仅把搜索栏的 DOM 位置从
scroll-view内部移到了外部
根因
问题出在 pageScroll 里的阈值比较逻辑:
// 旧代码(错误)
if (rect.top <= statusBarHeight) {
// 触发吸顶
}这里的 statusBarHeight 只代表状态栏高度。搜索栏在 scroll-view 内部时,scroll-view 的顶端就紧贴状态栏,所以这个阈值是正确的。
但把搜索栏提到 scroll-view 外部之后,页面结构变成了:
┌─────────────────────┐
│ 状态栏 │ ← statusBarHeight(约 44px)
├─────────────────────┤
│ 搜索栏(固定在外部) │ ← 约 60px,新增!
├─────────────────────┤
│ scroll-view │
│ Banner │
│ Tab ←─────────────│── 吸顶阈值仍是 statusBarHeight ✗
│ 内容瀑布 │
└─────────────────────┘
scroll-view 的起始位置下移了 60px,而代码里的阈值还是旧的 statusBarHeight,导致 Tab 的 rect.top 要再多走 60px 才能满足条件。
吸顶阈值 = scroll-view 实际顶端在视口中的 Y 坐标,不是固定值。只要 scroll-view 上方还有其他元素(不论是 fixed 还是 flow),阈值就得重新量。用硬编码的 statusBarHeight 只在 scroll-view 顶部恰好贴着状态栏时才正确。
正解
用 createSelectorQuery 动态量出头部元素的 bottom,把它作为吸顶阈值。rect.bottom 直接给出头部底边的视口 Y 坐标,正好等于 scroll-view 的起始位置。
第一步:给头部元素加 id
<view id="search-header" class="search-row" style="padding-top:{{statusBarHeight}}px">
<!-- 搜索栏内容 -->
</view>第二步:在 onLoad 里量一次,存到实例变量
onLoad: function () {
var that = this;
that.setData({ statusBarHeight: that.store.data.topLift });
setTimeout(() => {
wx.createSelectorQuery()
.select('#search-header')
.boundingClientRect((rect) => {
if (rect) {
that._stickyThreshold = rect.bottom; // 头部底边 = scroll-view 顶端
} else {
that._stickyThreshold = that.store.data.topLift + 60; // 布局未就绪时的兜底值
}
}).exec();
}, 500); // 等 layout 稳定,也可改用 onReady
},第三步:pageScroll 用动态阈值替换硬编码
pageScroll: function (e) {
var that = this;
var threshold = that._stickyThreshold || (that.store.data.topLift + 60);
wx.createSelectorQuery().in(this)
.select('#waterfall-tab')
.boundingClientRect((rect) => {
if (rect.top <= threshold) {
if (!that.data.tabFix) that.setData({ tabFix: true });
} else {
that.setData({ tabFix: false });
}
}).exec();
},rect.bottom 自动包含 padding、margin、状态栏偏移——不需要手算"statusBarHeight + 搜索栏高度 + 边距",也不会在不同机型上偏差。提供一个硬编码兜底值应对 query 在 layout 前执行的极端情况。
一句话外卖
scroll-view 上方每多一个元素,吸顶阈值就得重量一次——用
createSelectorQuery的rect.bottom取头部底边,永远比手算精确。