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

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<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>