158 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			158 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Python
		
	
	
	
| <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>
 |