guangchao.xu | 070005a | 2020-12-07 09:56:40 +0800 | [diff] [blame] | 1 | <template> |
| 2 | <view class=""> |
| 3 | <view class="u-sticky-wrap" :class="[elClass]" :style="{ |
| 4 | height: fixed ? height + 'px' : 'auto', |
| 5 | backgroundColor: bgColor |
| 6 | }"> |
| 7 | <view class="u-sticky" :style="{ |
| 8 | position: fixed ? 'fixed' : 'static', |
| 9 | top: stickyTop + 'px', |
| 10 | left: left + 'px', |
| 11 | width: width == 'auto' ? 'auto' : width + 'px', |
| 12 | zIndex: uZIndex |
| 13 | }"> |
| 14 | <slot></slot> |
| 15 | </view> |
| 16 | </view> |
| 17 | </view> |
| 18 | </template> |
| 19 | |
| 20 | <script> |
| 21 | /** |
| 22 | * sticky 吸顶 |
| 23 | * @description 该组件与CSS中position: sticky属性实现的效果一致,当组件达到预设的到顶部距离时, 就会固定在指定位置,组件位置大于预设的顶部距离时,会重新按照正常的布局排列。 |
| 24 | * @tutorial https://www.uviewui.com/components/sticky.html |
| 25 | * @property {String Number} offset-top 吸顶时与顶部的距离,单位rpx(默认0) |
| 26 | * @property {String Number} index 自定义标识,用于区分是哪一个组件 |
| 27 | * @property {Boolean} enable 是否开启吸顶功能(默认true) |
| 28 | * @property {String} bg-color 组件背景颜色(默认#ffffff) |
| 29 | * @property {String Number} z-index 吸顶时的z-index值(默认970) |
| 30 | * @property {String Number} h5-nav-height 导航栏高度,自定义导航栏时(无导航栏时需设置为0),需要传入此值,单位px(默认44) |
| 31 | * @event {Function} fixed 组件吸顶时触发 |
| 32 | * @event {Function} unfixed 组件取消吸顶时触发 |
| 33 | * @example <u-sticky offset-top="200"><view>塞下秋来风景异,衡阳雁去无留意</view></u-sticky> |
| 34 | */ |
| 35 | export default { |
| 36 | name: "u-sticky", |
| 37 | props: { |
| 38 | // 吸顶容器到顶部某个距离的时候,进行吸顶,在H5平台,NavigationBar为44px |
| 39 | offsetTop: { |
| 40 | type: [Number, String], |
| 41 | default: 0 |
| 42 | }, |
| 43 | //列表中的索引值 |
| 44 | index: { |
| 45 | type: [Number, String], |
| 46 | default: '' |
| 47 | }, |
| 48 | // 是否开启吸顶功能 |
| 49 | enable: { |
| 50 | type: Boolean, |
| 51 | default: true |
| 52 | }, |
| 53 | // h5顶部导航栏的高度 |
| 54 | h5NavHeight: { |
| 55 | type: [Number, String], |
| 56 | default: 44 |
| 57 | }, |
| 58 | // 吸顶区域的背景颜色 |
| 59 | bgColor: { |
| 60 | type: String, |
| 61 | default: '#ffffff' |
| 62 | }, |
| 63 | // z-index值 |
| 64 | zIndex: { |
| 65 | type: [Number, String], |
| 66 | default: '' |
| 67 | } |
| 68 | }, |
| 69 | data() { |
| 70 | return { |
| 71 | fixed: false, |
| 72 | height: 'auto', |
| 73 | stickyTop: 0, |
| 74 | elClass: this.$u.guid(), |
| 75 | left: 0, |
| 76 | width: 'auto', |
| 77 | }; |
| 78 | }, |
| 79 | watch: { |
| 80 | offsetTop(val) { |
| 81 | this.initObserver(); |
| 82 | }, |
| 83 | enable(val) { |
| 84 | if (val == false) { |
| 85 | this.fixed = false; |
| 86 | this.disconnectObserver('contentObserver'); |
| 87 | } else { |
| 88 | this.initObserver(); |
| 89 | } |
| 90 | } |
| 91 | }, |
| 92 | computed: { |
| 93 | uZIndex() { |
| 94 | return this.zIndex ? this.zIndex : this.$u.zIndex.sticky; |
| 95 | } |
| 96 | }, |
| 97 | mounted() { |
| 98 | this.initObserver(); |
| 99 | }, |
| 100 | methods: { |
| 101 | initObserver() { |
| 102 | if (!this.enable) return; |
| 103 | // #ifdef H5 |
| 104 | this.stickyTop = this.offsetTop != 0 ? uni.upx2px(this.offsetTop) + this.h5NavHeight : this.h5NavHeight; |
| 105 | // #endif |
| 106 | // #ifndef H5 |
| 107 | this.stickyTop = this.offsetTop != 0 ? uni.upx2px(this.offsetTop) : 0; |
| 108 | // #endif |
| 109 | |
| 110 | this.disconnectObserver('contentObserver'); |
| 111 | this.$uGetRect('.' + this.elClass).then((res) => { |
| 112 | this.height = res.height; |
| 113 | this.left = res.left; |
| 114 | this.width = res.width; |
| 115 | this.$nextTick(() => { |
| 116 | this.observeContent(); |
| 117 | }); |
| 118 | }); |
| 119 | }, |
| 120 | observeContent() { |
| 121 | this.disconnectObserver('contentObserver'); |
| 122 | const contentObserver = this.createIntersectionObserver({ |
| 123 | thresholds: [0.95, 0.98, 1] |
| 124 | }); |
| 125 | contentObserver.relativeToViewport({ |
| 126 | top: -this.stickyTop |
| 127 | }); |
| 128 | contentObserver.observe('.' + this.elClass, res => { |
| 129 | if (!this.enable) return; |
| 130 | this.setFixed(res.boundingClientRect.top); |
| 131 | }); |
| 132 | this.contentObserver = contentObserver; |
| 133 | }, |
| 134 | setFixed(top) { |
| 135 | const fixed = top < this.stickyTop; |
| 136 | if (fixed) this.$emit('fixed', this.index); |
| 137 | else if(this.fixed) this.$emit('unfixed', this.index); |
| 138 | this.fixed = fixed; |
| 139 | }, |
| 140 | disconnectObserver(observerName) { |
| 141 | const observer = this[observerName]; |
| 142 | observer && observer.disconnect(); |
| 143 | }, |
| 144 | }, |
| 145 | beforeDestroy() { |
| 146 | this.disconnectObserver('contentObserver'); |
| 147 | } |
| 148 | }; |
| 149 | </script> |
| 150 | |
| 151 | <style scoped lang="scss"> |
| 152 | @import "../../libs/css/style.components.scss"; |
| 153 | |
| 154 | .u-sticky { |
| 155 | z-index: 9999999999; |
| 156 | } |
| 157 | </style> |