导航栏吸顶
当页面滚动超过导航栏时,导航栏依附固定在页面顶端
下面介绍两种方式吧, 一种是 position: sticky
, 另一种是getBoundingClientRect()
position: sticky 实现
.stickyPostion {
postion: sticky;
top: 0;
}
使用条件:
- 父元素不能
overflow:hidden
或者overflow:auto
属性。 - 必须指定
top
、bottom
、left
、right
4个值之一,否则只会处于相对定位 - 父元素的高度不能低于sticky元素的高度
- sticky元素仅在其父元素内生效
sticky 坑
- 兼容性不太好
- 不能触发
BFC
getBoundingClientRect()
getBoundingClientRect用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置。
getBoundingClientRect是DOM元素到浏览器可视范围的距离(不包含文档卷起的部分)。
- 获取当前 nav 到浏览器顶端的距离。
- 直接监听scroll 事件,
- 用div包裹住nav导航栏,我们只需要获取当前div到顶端的距离即可,等到时 top值为0的时候,设置子级的position:fixed, 还原只需取消掉子级的position即可
- 如果没有用div 包裹 nav导航栏的话,等top 值为0,就无法还原了,position 一直为fix, getBoundingClientRect.top值一直为0;
结构应该如下:
<div ref={this.stickyRef} id="sticky">
<div className={isFixed ? styles.fix : styles.static}> // 这个为nav
<div>吸顶效果</div>
</div>
</div>
componentDidMount() {
const top = this.stickyRef.current.getBoundingClientRect().top;
this.setState({
initPostionTop: top
})
window.addEventListener('scroll', this.handleScroll);
}
handleScroll = (e) => {
const { isFixed, initPostionTop } = this.state
const scrollTop = e.srcElement.body.scrollTop || e.srcElement.documentElement.scrollTop;
// 这里做了个优化,防止频繁setState, 如果一开始为吸顶,以及滚动距离大于 nav 到顶部的距离的时候,
// 或者 (不吸顶同时当前滚动距离小于nav到顶部的距离时)我们操作
if(isFixed && scrollTop >= initPostionTop || !isFixed && scrollTop <= initPostionTop) {
return ;
} else {
this.setState({
isFixed: scrollTop > initPostionTop
})
}
}
小程序 (我使用的是Taro)
小程序不支持
window.addEventListener('scroll', this.handleScroll);
,只有onPageScroll
下面是官方说明
注意:请只在需要的时候才在 page 中定义此方法,不要定义空方法。以减少不必要的事件派发对渲染层-逻辑层通信的影响。注意:请避免在 onPageScroll 中过于频繁的执行 this.setState() 等引起逻辑层-渲染层通信的操作。尤其是每次传输大量数据,会影响通信耗时。
taro获取节点有点坑
componentDidMount() {
// 注意this.$scope
const query = Taro.createSelectorQuery().in(this.$scope);
query.select('#sticky').boundingClientRect((rect) => {
this.setState({
initPostionTop: rect.top
})
}).exec();
}
onPageScroll 代码基本一样
坑
- 这种方式有个坑,当吸顶的一瞬间,会发生抖动,这是因为
position
变成fixed
的时候脱离了文档流。
解决方案是 在postion:fixed;
的元素里添加transform: translateZ(0);
,我想应该是开启gpu 渲染,所以变得顺滑了吧
关于优化
h5 优化,可以用另一种方式,就是用 监听滚动的时候使用 节流, 可以用 lodash.throttle
window.addEventListener('scroll', _.throttle(self.handleScrollThree, 50));