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