vertical-scroll.vue 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. <template>
  2. <div class="nut-vert-scroll" ref="wrapper">
  3. <div class="nut-vert-list" ref="list" :style="{'min-height': listMinHeightStyle}">
  4. <div class="nut-vert-pulldown" v-if="isFirstPull">
  5. <div class="nut-vert-pulldown-txt" v-if="!isLoading">{{pulldownTxt}}</div>
  6. <div class="nut-vert-pulldown-status" v-else>
  7. <span class="nut-vert-loading"></span>
  8. <span class="nut-vert-loading-txt">加载中...</span>
  9. </div>
  10. </div>
  11. <slot name="list"></slot>
  12. <div class="nut-vert-loadmore" >
  13. <template v-if="!isUnMore && isShowLoadMore">
  14. <!-- <div class="nut-vert-load-txt" >{{loadMoreTxt}}</div> -->
  15. <div class="nut-vert-load-status" v-if="isLoading">
  16. <span class="nut-vert-loading"></span>
  17. <span class="nut-vert-loading-txt">加载中...</span>
  18. </div>
  19. </template>
  20. <template v-else-if="isUnMore">
  21. <div class="nut-vert-unloadmore-txt">{{unloadMoreTxt}}</div>
  22. </template>
  23. </div>
  24. </div>
  25. </div>
  26. </template>
  27. <script>
  28. export default {
  29. name:'nut-vert-scroll',
  30. props: {
  31. stretch: {
  32. type: Number,
  33. default: 100
  34. },
  35. isUnMore: {
  36. type: Boolean,
  37. default: false
  38. },
  39. isLoading: {
  40. type: Boolean,
  41. default: false
  42. },
  43. pulldownTxt: {
  44. type: String,
  45. default: '下拉刷新'
  46. },
  47. loadMoreTxt: {
  48. type: String,
  49. default: '上拉加载'
  50. },
  51. unloadMoreTxt: {
  52. type: String,
  53. default: '没有更多了'
  54. },
  55. threshold: {
  56. type: Number,
  57. default: 100
  58. },
  59. propsTime: {
  60. type:Number,
  61. default: 0
  62. },
  63. scrollTo: {
  64. type: Number,
  65. default: 1
  66. }
  67. },
  68. watch: {
  69. 'isLoading': function(status) {
  70. if (!status && this.realMove === 0) {
  71. clearTimeout(this.timer);
  72. this.timer = setTimeout(() => {
  73. this.setTransform(this.realMove, 'end', null);
  74. }, this.propsTime);
  75. }
  76. },
  77. 'isUnMore': function() {
  78. this.isShow();
  79. },
  80. 'scrollTo': function(val) {
  81. if (typeof val === 'number' && !isNaN(val) && val <= 0 ) {
  82. this.setTransform(val, null, 500);
  83. this.$emit('scrollToCbk');
  84. }
  85. }
  86. },
  87. data() {
  88. return {
  89. touchParams: {
  90. startY: 0,
  91. endY: 0,
  92. startTime: 0,
  93. endTime: 0
  94. },
  95. translateY: 0,
  96. scrollDistance: 0,
  97. timer: null,
  98. timerEmit: null,
  99. realMove: 0,
  100. isShowLoadMore: false,
  101. listMinHeightStyle: 'auto',
  102. isFirstPull: true
  103. }
  104. },
  105. methods: {
  106. isShow() {
  107. let wrapH = this.$refs.wrapper.offsetHeight;
  108. let listH = this.$refs.list.offsetHeight;
  109. if (wrapH < listH) {
  110. this.isShowLoadMore = true;
  111. this.listMinHeightStyle = 'auto'
  112. } else {
  113. this.isShowLoadMore = false;
  114. this.isFirstPull = true;
  115. this.listMinHeightStyle = `${wrapH}px`;
  116. }
  117. },
  118. setTransform(translateY = 0, type, time = 500) {
  119. if (type === 'end') {
  120. this.$refs.list.style.webkitTransition = `transform ${time}ms cubic-bezier(0.19, 1, 0.22, 1)`;
  121. } else {
  122. this.$refs.list.style.webkitTransition = '';
  123. }
  124. this.$refs.list.style.webkitTransform = `translate3d(0, ${translateY}px, 0)`;
  125. this.scrollDistance = translateY;
  126. },
  127. setMove(move, type, time) {
  128. let updateMove = move + this.translateY;
  129. let h = this.$refs.wrapper.offsetHeight;
  130. let maxMove = -this.$refs.list.offsetHeight + h;
  131. if (type === 'end') {
  132. if (updateMove > 0) {
  133. this.realMove = 0;
  134. if ((!this.isShowLoadMore || this.isFirstPull ) && !this.isLoading && updateMove > 20) {
  135. updateMove = 50;
  136. clearTimeout(this.timerEmit);
  137. this.timerEmit = setTimeout(() => {
  138. this.$emit('pulldown');
  139. }, time / 2);
  140. } else {
  141. this.isFirstPull = true;
  142. updateMove = 0;
  143. }
  144. } else if (updateMove < 0 && updateMove < maxMove + this.threshold) {
  145. if (updateMove < maxMove) {
  146. updateMove = maxMove;
  147. }
  148. this.realMove = maxMove;
  149. if (!this.isLoading && !this.isUnMore) {
  150. //clearTimeout(this.timerEmit);
  151. //this.timerEmit = setTimeout(() => {
  152. this.$emit('loadMore');
  153. // }, time / 2);
  154. }
  155. }
  156. // if (updateMove == 50 && !this.isLoading) {
  157. // clearTimeout(this.timer);
  158. // this.timer = setTimeout(() => {
  159. // this.setTransform(this.realMove, 'end', null);
  160. // }, 3000);
  161. // }
  162. this.setTransform(updateMove, type, time)
  163. } else {
  164. if (updateMove > 0 && updateMove > this.stretch) {
  165. updateMove = this.stretch;
  166. } else if (updateMove < maxMove - this.stretch) {
  167. updateMove = maxMove - this.stretch;
  168. }
  169. this.setTransform(updateMove, null, null);
  170. }
  171. },
  172. touchStart(event) {
  173. // event.preventDefault();
  174. let changedTouches = event.changedTouches[0];
  175. this.touchParams.startY = changedTouches.pageY;
  176. this.touchParams.startTime = event.timestamp || Date.now();
  177. this.translateY = this.scrollDistance;
  178. },
  179. touchMove(event) {
  180. event.preventDefault();
  181. let changedTouches = event.changedTouches[0];
  182. this.touchParams.lastY = changedTouches.pageY;
  183. this.touchParams.lastTime = event.timestamp || Date.now();
  184. let move = this.touchParams.lastY - this.touchParams.startY;
  185. if (move < 0 && this.isShowLoadMore && this.isFirstPull) {
  186. this.isFirstPull = false;
  187. }
  188. this.setMove(move);
  189. },
  190. touchEnd(event) {
  191. // event.preventDefault();
  192. let changedTouches = event.changedTouches[0];
  193. this.touchParams.lastY = changedTouches.pageY;
  194. this.touchParams.lastTime = event.timestamp || Date.now();
  195. let move = this.touchParams.lastY - this.touchParams.startY;
  196. let moveTime = this.touchParams.lastTime - this.touchParams.startTime;
  197. let h = this.$refs.wrapper.offsetHeight;
  198. let maxMove = -this.$refs.list.offsetHeight + h;
  199. if (moveTime <= 300) {
  200. move = move * 2;
  201. if (move < 0 && move < maxMove) {
  202. move = maxMove;
  203. }
  204. moveTime = moveTime + 500;
  205. this.setMove(move, 'end', moveTime);
  206. } else {
  207. this.setMove(move, 'end');
  208. }
  209. }
  210. },
  211. mounted() {
  212. this.$nextTick(() => {
  213. this.isShow();
  214. // 监听
  215. this.$el.addEventListener('touchstart', this.touchStart);
  216. this.$el.addEventListener('touchmove', this.touchMove);
  217. this.$el.addEventListener('touchend', this.touchEnd);
  218. });
  219. },
  220. beforeDestroy() {
  221. // 移除监听
  222. this.$el.removeEventListener('touchstart', this.touchStart);
  223. this.$el.removeEventListener('touchmove', this.touchMove);
  224. this.$el.removeEventListener('touchend', this.touchEnd);
  225. clearTimeout(this.timer);
  226. clearTimeout(this.timerEmit);
  227. }
  228. }
  229. </script>