yunshangxie/tuniao-ui/components/tn-sticky/tn-sticky.vue

187 lines
4.9 KiB
Vue
Raw Permalink Normal View History

2023-12-25 17:56:30 +08:00
<template>
<view class="tn-sticky-class">
<view
class="tn-sticky__wrap"
:class="[stickyClass]"
:style="[stickyStyle]"
>
<view
class="tn-sticky__item"
:style="{
position: fixed ? 'fixed' : 'static',
top: stickyTop + 'px',
left: left + 'px',
width: width === 'auto' ? 'auto' : width + 'px',
zIndex: elZIndex
}"
>
<slot></slot>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'tn-sticky',
props: {
// 吸顶容器到顶部某个距离的时候进行吸顶
// 在H5中customNavBar的高度为45px
offsetTop: {
type: Number,
default: 0
},
// H5顶部导航栏的高度
h5NavHeight: {
type: Number,
default: 45
},
// 自定义顶部导航栏高度
customNavHeight: {
type: Number,
default: 0
},
// 是否开启吸顶
enabled: {
type: Boolean,
default: true
},
// 吸顶容器的背景颜色
backgroundColor: {
type: String,
default: '#FFFFFF'
},
// z-index
zIndex: {
type: Number,
default: 0
},
// 索引值,区分不同的吸顶组件
index: {
type: [String, Number],
default: ''
}
},
computed: {
elZIndex() {
return this.zIndex ? this.zIndex : this.$tn.zIndex.sticky
},
backgroundColorStyle() {
return this.$tn.color.getBackgroundColorStyle(this.backgroundColor)
},
backgroundColorClass() {
return this.$tn.color.getBackgroundColorInternalClass(this.backgroundColor)
},
stickyClass() {
let clazz = ''
clazz += this.elClass
if (this.backgroundColorClass) {
clazz += ` ${this.backgroundColorClass}`
}
return clazz
},
stickyStyle() {
let style = {}
style.height = this.fixed ? this.height + 'px' : 'auto'
if (this.backgroundColorStyle) {
style.color = this.backgroundColorStyle
}
if (this.elZIndex) {
style.zIndex = this.elZIndex
}
return style
}
},
data() {
return {
// 监听组件别名
stickyObserverName: 'tnStickyObserver',
// 组件的唯一编号
elClass: this.$tn.uuid(),
// 是否固定
fixed: false,
// 高度
height: 'auto',
// 宽度
width: 'auto',
// 距离顶部的距离
stickyTop: 0,
// 左边距离
left: 0
}
},
watch: {
offsetTop(val) {
this.initObserver()
},
enabled(val) {
if (val === false) {
this.fixed = false
this.disconnectObserver(this.stickyObserverName)
} else {
this.initObserver()
}
},
customNavHeight(val) {
this.initObserver()
}
},
mounted() {
this.initObserver()
},
methods: {
// 初始化监听组件的布局状态
initObserver() {
if (!this.enabled) return
// #ifdef H5
this.stickyTop = this.offsetTop != 0 ? uni.upx2px(this.offsetTop) + this.h5NavHeight : this.h5NavHeight
// #endif
// #ifndef H5
this.stickyTop = this.offsetTop != 0 ? uni.upx2px(this.offsetTop) + this.customNavHeight : this.customNavHeight
// #endif
this.disconnectObserver(this.stickyObserverName)
this._tGetRect('.' + this.elClass).then((res) => {
this.height = res.height
this.left = res.left
this.width = res.width
this.$nextTick(() => {
this.connectObserver()
})
})
},
// 监听组件的布局状态
connectObserver() {
this.disconnectObserver(this.stickyObserverName)
// 组件内获取布局状态不能用uni.createIntersectionObserver而必须用this.createIntersectionObserver
const contentObserver = this.createIntersectionObserver({
thresholds: [0.95, 0.98, 1]
})
contentObserver.relativeToViewport({
top: -this.stickyTop
})
contentObserver.observe('.' + this.elClass, res => {
if (!this.enabled) return
this.setFixed(res.boundingClientRect.top)
})
this[this.stickyObserverName] = contentObserver
},
// 设置是否固定
setFixed(top) {
const fixed = top < this.stickyTop
if (fixed) this.$emit('fixed', this.index)
else if (this.fixed) this.$emit('unfixed', this.index)
this.fixed = fixed
},
// 停止监听组件的布局状态
disconnectObserver(observerName) {
const observer = this[observerName]
observer && observer.disconnect()
}
}
}
</script>
<style>
</style>