187 lines
4.9 KiB
Vue
187 lines
4.9 KiB
Vue
|
<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>
|