u-sticky.vue 4.2 KB

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