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