infiniteloading.vue 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. <template>
  2. <div class="nut-infiniteloading" ref="scroller" @touchstart="touchStartHandle($event)" @touchmove="touchMoveHandle($event)">
  3. <slot></slot>
  4. <div class="load-more">
  5. <div class="bottom-tips">
  6. <template v-if="isLoading"> <i class="loading-hint"></i><span class="loading-txt">加载中...</span> </template>
  7. <span v-else-if="!hasMore" class="tips-txt">{{ unloadMoreTxt }}</span>
  8. </div>
  9. </div>
  10. </div>
  11. </template>
  12. <script>
  13. export default {
  14. name: 'nut-infiniteloading',
  15. props: {
  16. hasMore: {
  17. type: Boolean,
  18. default: true,
  19. },
  20. isLoading: {
  21. type: Boolean,
  22. default: false,
  23. },
  24. threshold: {
  25. type: Number,
  26. default: 200,
  27. },
  28. useWindow: {
  29. type: Boolean,
  30. default: true,
  31. },
  32. useCapture: {
  33. type: Boolean,
  34. default: false,
  35. },
  36. isShowMod: {
  37. type: Boolean,
  38. default: false,
  39. },
  40. unloadMoreTxt: {
  41. type: String,
  42. default: '哎呀,这里是底部了啦',
  43. },
  44. scrollChange: {
  45. type: Function,
  46. },
  47. containerId: {
  48. type: String,
  49. default: '',
  50. },
  51. },
  52. data() {
  53. return {
  54. startX: 0,
  55. startY: 0,
  56. diffX: 0,
  57. diffY: 0,
  58. beforeScrollTop: 0,
  59. };
  60. },
  61. mounted: function () {
  62. const parentElement = this.getParentElement(this.$el);
  63. let scrollEl = window;
  64. if (this.useWindow === false) {
  65. scrollEl = parentElement;
  66. }
  67. this.scrollEl = scrollEl;
  68. this.scrollListener();
  69. },
  70. methods: {
  71. touchStartHandle(e) {
  72. try {
  73. this.startX = Number(e.changedTouches[0].pageX);
  74. this.startY = Number(e.changedTouches[0].pageY);
  75. } catch (e) {
  76. console.log(e.message);
  77. }
  78. },
  79. touchMoveHandle(e) {
  80. let endX = Number(e.changedTouches[0].pageX);
  81. let endY = Number(e.changedTouches[0].pageY);
  82. this.diffX = endX - this.startX;
  83. this.diffY = endY - this.startY;
  84. },
  85. getParentElement(el) {
  86. if (this.containerId) {
  87. return document.querySelector(`#${this.containerId}`);
  88. }
  89. return el && el.parentNode;
  90. },
  91. scrollListener() {
  92. this.scrollEl.addEventListener('scroll', this.handleScroll, this.useCapture);
  93. window.addEventListener('resize', this.handleScroll, this.useCapture);
  94. },
  95. requestAniFrame() {
  96. return (
  97. window.requestAnimationFrame ||
  98. window.webkitRequestAnimationFrame ||
  99. window.mozRequestAnimationFrame ||
  100. function (callback) {
  101. window.setTimeout(callback, 1000 / 60);
  102. }
  103. );
  104. },
  105. handleScroll() {
  106. this.requestAniFrame()(() => {
  107. if (!this.isScrollAtBottom() || !this.hasMore || this.isLoading || !this.isShowMod) {
  108. return false;
  109. } else {
  110. this.$emit('loadmore');
  111. }
  112. });
  113. },
  114. calculateTopPosition(el) {
  115. if (!el) {
  116. return 0;
  117. }
  118. return el.offsetTop + this.calculateTopPosition(el.offsetParent);
  119. },
  120. getWindowScrollTop() {
  121. return window.pageYOffset !== undefined
  122. ? window.pageYOffset
  123. : (document.documentElement || document.body.parentNode || document.body).scrollTop;
  124. },
  125. isScrollAtBottom() {
  126. let offsetDistance;
  127. let resScrollTop = 0;
  128. const windowScrollTop = this.getWindowScrollTop();
  129. if (this.useWindow) {
  130. offsetDistance = this.calculateTopPosition(this.$refs.scroller) + this.$refs.scroller.offsetHeight - windowScrollTop - window.innerHeight;
  131. } else {
  132. const { scrollHeight, clientHeight, scrollTop } = this.scrollEl;
  133. offsetDistance = scrollHeight - clientHeight - scrollTop;
  134. resScrollTop = scrollTop;
  135. }
  136. this.$emit('scrollChange', this.useWindow ? windowScrollTop : resScrollTop);
  137. // 保证是往下滑动的
  138. let beforeScrollTop = this.beforeScrollTop;
  139. this.beforeScrollTop = windowScrollTop;
  140. return offsetDistance <= this.threshold && windowScrollTop >= this.beforeScrollTop;
  141. },
  142. },
  143. activated() {
  144. if (this.keepAlive) {
  145. this.keepAlive = false;
  146. this.scrollListener();
  147. }
  148. },
  149. deactivated() {
  150. this.keepAlive = true;
  151. this.scrollEl.removeEventListener('scroll', this.handleScroll, this.useCapture);
  152. window.removeEventListener('resize', this.handleScroll, this.useCapture);
  153. },
  154. destroyed() {
  155. this.scrollEl.removeEventListener('scroll', this.handleScroll, this.useCapture);
  156. window.removeEventListener('resize', this.handleScroll, this.useCapture);
  157. },
  158. };
  159. </script>