前言
CSS中有定位属性position有一个属性值sticky粘性定位,该属性大家应该都不陌生,它可以用于固定头部导航等场景,如果我们用CSS去设置粘性定位,需要考虑下兼容性,可能还要设置其他元素样式。我们可以封装一个粘性组件来实现粘性定位的效果,方便开发中的使用与维护。本节我们就用vue2+ts自定义一个粘性组件。
粘性组件
粘性定位的特点是元素在一定区域内滚动表现为relative相对定位,而当超出对应区域滚动又会表现为fixed固定定位。这里面有两大元素——滚动与定位:
- 滚动指的滚动条,我们需要监听滚动事件,当页面滚动条滚动到某个位置,元素就要固定起来,没有的话就随页面滚动。
- 定位指的position,根据粘性定位的效果,position的值要在relative与fixed之间替换。替换的条件就是滚动条滚动到设定的位置,这就要求我们获取元素的位置属性,对此我们可以借助getBoundingClientRect。
getBoundingClientRect()可以获取元素在视口位置与大小的方法,返回值是一个对象,里面包含了:
top:元素上边框相对于视口顶部的距离。
right:右边框相对于左侧的距离。
bottom:下边框相对于顶部的距离。
left:左边框相对于左侧的距离。
x:左边缘相对于左侧的距离。
y:上边缘相对于顶部的距离。
width:元素宽度。
height:元素高度。
一个方法基本位置属性都获取到了,判断固定位置的数值我们可以变为一个参数用props传递,以此来复用。这里我们设定为头部导航栏情景,就用stickyTop去表述滚动的位置,除此参数我们可以自定义一些props。
<template>
<div :style="{ height: height, zIndex: zIndex }">
<div
:class="className"
:style="{
top: isSticky ? stickyTop + 'px' : '',
zIndex: zIndex,
position: position,
width: width,
height: height
}"
>
<slot>
</slot>
</div>
</div>
</template>
<script lang="ts">
//这里引入了装饰器库 运用了装饰器的写法 我们也可以直接用选项式Api的写法
import { Component, Prop, Vue } from 'vue-property-decorator'
@Component({
name: 'Sticky'
})
export default class extends Vue {
//设定的距离
@Prop({ default: 0 }) private stickyTop!: number
@Prop({ default: 1 }) private zIndex!: number
@Prop({ default: '' }) private className!: string
private active = false
private position = 'relative'
private isSticky = false
private width = 'auto'
private height = 'auto'
}
</script>
定义好props下面我们就是监听滚动事件进行判断,根据判断的结果去触发不同的函数。
public mounted() {
//获取元素高度
this.height = this.$el.getBoundingClientRect().height.toString() + 'px'
//监听滚动事件
window.addEventListener('scroll', this.handleScroll)
}
public activated() {
this.handleScroll()
}
public destroyed() {
window.removeEventListener('scroll', this.handleScroll)
}
private sticky() {
if (this.active) {
return
}
this.position = 'fixed'
this.active = true
this.isSticky = true
}
//重置
private handleReset() {
if (!this.active) {
return
}
this.position = 'relative'
this.width = 'auto'
this.active = false
this.isSticky = false
}
private handleScroll() {
const width = this.$el.getBoundingClientRect().width
this.width = width.toString() + 'px' || 'auto'
const offsetTop = this.$el.getBoundingClientRect().top
//滚动到一定的位置改变position
if (offsetTop < this.stickyTop) {
this.sticky()
return
}
this.handleReset()
}
到此我们就实现了一个粘性组件,我们在其他页面注册一下该组件就能使用了,除此之外我们可以优化一下,监听下resize事件,让包裹内容的div进行自适应宽度。
private handleResize() {
//如果元素为固定 需要重新设置下width
if (this.isSticky) {
this.width = this.$el.getBoundingClientRect().width.toString() + 'px'
}
}
总结
以上就是粘性组件的实现过程,该组件适用范围不是特别广,我们也可以自己优化下,让其更适合自己的项目。