You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

158 lines
5.6 KiB
Vue

<template>
<view class="">
<view class="u-sticky-wrap" :class="[elClass]" :style="{
height: fixed ? height + 'px' : 'auto',
backgroundColor: bgColor
}">
<view class="u-sticky" :style="{
position: fixed ? 'fixed' : 'static',
top: stickyTop + 'px',
left: left + 'px',
width: width == 'auto' ? 'auto' : width + 'px',
zIndex: uZIndex
}">
<slot></slot>
</view>
</view>
</view>
</template>
<script>
/**
* sticky 吸顶
* @description 该组件与CSS中position: sticky属性实现的效果一致当组件达到预设的到顶部距离时 就会固定在指定位置组件位置大于预设的顶部距离时会重新按照正常的布局排列
* @tutorial https://www.uviewui.com/components/sticky.html
* @property {String Number} offset-top 吸顶时与顶部的距离单位rpx默认0
* @property {String Number} index 自定义标识用于区分是哪一个组件
* @property {Boolean} enable 是否开启吸顶功能默认true
* @property {String} bg-color 组件背景颜色默认#ffffff
* @property {String Number} z-index 吸顶时的z-index值默认970
* @property {String Number} h5-nav-height 导航栏高度自定义导航栏时(无导航栏时需设置为0)需要传入此值单位px默认44
* @event {Function} fixed 组件吸顶时触发
* @event {Function} unfixed 组件取消吸顶时触发
* @example <u-sticky offset-top="200"><view>塞下秋来风景异衡阳雁去无留意</view></u-sticky>
*/
export default {
name: "u-sticky",
props: {
// 吸顶容器到顶部某个距离的时候进行吸顶在H5平台NavigationBar为44px
offsetTop: {
type: [Number, String],
default: 0
},
//列表中的索引值
index: {
type: [Number, String],
default: ''
},
// 是否开启吸顶功能
enable: {
type: Boolean,
default: true
},
// h5顶部导航栏的高度
h5NavHeight: {
type: [Number, String],
default: 44
},
// 吸顶区域的背景颜色
bgColor: {
type: String,
default: '#ffffff'
},
// z-index值
zIndex: {
type: [Number, String],
default: ''
}
},
data() {
return {
fixed: false,
height: 'auto',
stickyTop: 0,
elClass: this.$u.guid(),
left: 0,
width: 'auto',
};
},
watch: {
offsetTop(val) {
this.initObserver();
},
enable(val) {
if (val == false) {
this.fixed = false;
this.disconnectObserver('contentObserver');
} else {
this.initObserver();
}
}
},
computed: {
uZIndex() {
return this.zIndex ? this.zIndex : this.$u.zIndex.sticky;
}
},
mounted() {
this.initObserver();
},
methods: {
initObserver() {
if (!this.enable) 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) : 0;
// #endif
this.disconnectObserver('contentObserver');
this.$uGetRect('.' + this.elClass).then((res) => {
this.height = res.height;
this.left = res.left;
this.width = res.width;
this.$nextTick(() => {
this.observeContent();
});
});
},
observeContent() {
this.disconnectObserver('contentObserver');
const contentObserver = this.createIntersectionObserver({
thresholds: [0.95, 0.98, 1]
});
contentObserver.relativeToViewport({
top: -this.stickyTop
});
contentObserver.observe('.' + this.elClass, res => {
if (!this.enable) return;
this.setFixed(res.boundingClientRect.top);
});
this.contentObserver = 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();
},
},
beforeDestroy() {
this.disconnectObserver('contentObserver');
}
};
</script>
<style scoped lang="scss">
@import "../../libs/css/style.components.scss";
.u-sticky {
z-index: 9999999999;
}
</style>