tui-sticky.vue 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. <template>
  2. <view class="tui-sticky-class">
  3. <!--sticky 容器-->
  4. <view :style="{height: stickyHeight,backgroundColor:backgroundColor}" v-if="isFixed"></view>
  5. <view :class="{'tui-sticky-fixed':isFixed}" :style="{top:isFixed?stickyTop+'px':'auto'}">
  6. <slot name="header"></slot>
  7. </view>
  8. <!--sticky 容器-->
  9. <!--内容-->
  10. <slot name="content"></slot>
  11. </view>
  12. </template>
  13. <script>
  14. export default {
  15. name: "tuiSticky",
  16. props: {
  17. scrollTop: {
  18. type: Number
  19. },
  20. //吸顶时与顶部的距离,单位px
  21. stickyTop: {
  22. type: [Number, String]
  23. // #ifndef H5
  24. ,default: 0
  25. // #endif
  26. // #ifdef H5
  27. ,default: 44
  28. // #endif
  29. },
  30. //是否指定容器,即内容放置插槽content内
  31. container: {
  32. type: Boolean,
  33. default: false
  34. },
  35. //是否是原生自带header
  36. isNativeHeader: {
  37. type: Boolean,
  38. default: true
  39. },
  40. //吸顶容器 高度 rpx
  41. stickyHeight: {
  42. type: String,
  43. default: "auto"
  44. },
  45. //占位容器背景颜色
  46. backgroundColor: {
  47. type: String,
  48. default: "transparent"
  49. },
  50. //是否重新计算[有异步加载时使用,设置大于0的数]
  51. recalc: {
  52. type: Number,
  53. default: 0
  54. },
  55. //列表中的索引值
  56. index: {
  57. type: [Number, String],
  58. default: 0
  59. }
  60. },
  61. watch: {
  62. scrollTop(newValue, oldValue) {
  63. if (this.initialize != 0) {
  64. this.updateScrollChange(() => {
  65. this.updateStickyChange();
  66. this.initialize = 0
  67. });
  68. } else {
  69. this.updateStickyChange();
  70. }
  71. },
  72. recalc(newValue, oldValue) {
  73. this.updateScrollChange(() => {
  74. this.updateStickyChange();
  75. this.initialize = 0;
  76. });
  77. }
  78. },
  79. created() {
  80. this.initialize = this.recalc
  81. },
  82. mounted() {
  83. setTimeout(() => {
  84. this.updateScrollChange();
  85. }, 20)
  86. },
  87. data() {
  88. return {
  89. timer: null,
  90. top: 0,
  91. height: 0,
  92. isFixed: false,
  93. initialize: 0 //重新初始化
  94. };
  95. },
  96. methods: {
  97. updateStickyChange() {
  98. const top = this.top;
  99. const height = this.height;
  100. const scrollTop = this.scrollTop
  101. let stickyTop = this.stickyTop
  102. // #ifdef H5
  103. if (this.isNativeHeader) {
  104. stickyTop = stickyTop - 44
  105. stickyTop = stickyTop < 0 ? 0 : stickyTop
  106. }
  107. // #endif
  108. if (this.container) {
  109. this.isFixed = (scrollTop + stickyTop >= top && scrollTop + stickyTop < top + height) ? true : false
  110. } else {
  111. this.isFixed = scrollTop + stickyTop >= top ? true : false
  112. }
  113. //是否吸顶
  114. this.$emit("sticky", {
  115. isFixed: this.isFixed,
  116. index: this.index
  117. })
  118. },
  119. updateScrollChange(callback) {
  120. if (this.timer) {
  121. clearTimeout(this.timer)
  122. this.timer = null
  123. }
  124. this.timer = setTimeout(() => {
  125. const className = '.tui-sticky-class';
  126. const query = uni.createSelectorQuery().in(this);
  127. query.select(className).boundingClientRect((res) => {
  128. if (res) {
  129. this.top = res.top + (this.scrollTop || 0);
  130. this.height = res.height;
  131. callback && callback();
  132. this.$emit("change", {
  133. index: Number(this.index),
  134. top: this.top
  135. })
  136. }
  137. }).exec()
  138. }, 0)
  139. }
  140. }
  141. }
  142. </script>
  143. <style scoped>
  144. .tui-sticky-fixed {
  145. width: 100%;
  146. position: fixed;
  147. left: 0;
  148. z-index: 888;
  149. }
  150. </style>