u-sticky.vue 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  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. <script>
  20. /**
  21. * sticky 吸顶
  22. * @description 该组件与CSS中position: sticky属性实现的效果一致,当组件达到预设的到顶部距离时, 就会固定在指定位置,组件位置大于预设的顶部距离时,会重新按照正常的布局排列。
  23. * @tutorial https://www.uviewui.com/components/sticky.html
  24. * @property {String Number} offset-top 吸顶时与顶部的距离,单位rpx(默认0)
  25. * @property {String Number} index 自定义标识,用于区分是哪一个组件
  26. * @property {Boolean} enable 是否开启吸顶功能(默认true)
  27. * @property {String} bg-color 组件背景颜色(默认#ffffff)
  28. * @property {String Number} z-index 吸顶时的z-index值(默认970)
  29. * @property {String Number} h5-nav-height 导航栏高度,自定义导航栏时(无导航栏时需设置为0),需要传入此值,单位px(默认44)
  30. * @event {Function} fixed 组件吸顶时触发
  31. * @event {Function} unfixed 组件取消吸顶时触发
  32. * @example <u-sticky offset-top="200"><view>塞下秋来风景异,衡阳雁去无留意</view></u-sticky>
  33. */
  34. export default {
  35. name: "u-sticky",
  36. emits: ["fixed", "unfixed"],
  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. this.disconnectObserver('contentObserver');
  110. this.$uGetRect('.' + this.elClass).then((res) => {
  111. this.height = res.height;
  112. this.left = res.left;
  113. this.width = res.width;
  114. this.$nextTick(() => {
  115. this.observeContent();
  116. });
  117. });
  118. },
  119. observeContent() {
  120. this.disconnectObserver('contentObserver');
  121. const contentObserver = uni.createIntersectionObserver(this,{
  122. thresholds: [0.95, 0.98, 1]
  123. });
  124. contentObserver.relativeToViewport({
  125. top: -this.stickyTop
  126. });
  127. contentObserver.observe('.' + this.elClass, res => {
  128. if (!this.enable) return;
  129. this.setFixed(res.boundingClientRect.top);
  130. });
  131. this.contentObserver = contentObserver;
  132. },
  133. setFixed(top) {
  134. const fixed = top < this.stickyTop;
  135. if (fixed) this.$emit('fixed', this.index);
  136. else if(this.fixed) this.$emit('unfixed', this.index);
  137. this.fixed = fixed;
  138. },
  139. disconnectObserver(observerName) {
  140. const observer = this[observerName];
  141. observer && observer.disconnect();
  142. },
  143. },
  144. // #ifdef VUE2
  145. beforeDestroy() {
  146. this.disconnectObserver('contentObserver');
  147. },
  148. // #endif
  149. // #ifdef VUE3
  150. beforeUnmount() {
  151. this.disconnectObserver('contentObserver');
  152. },
  153. // #endif
  154. };
  155. </script>
  156. <style scoped lang="scss">
  157. @import "../../libs/css/style.components.scss";
  158. .u-sticky {
  159. z-index: 9999999999;
  160. }
  161. </style>