微信

scroll.vue 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. <template>
  2. <div ref="wrapper" class="list-wrapper">
  3. <div class="scroll-content">
  4. <div ref="listWrapper">
  5. <slot></slot>
  6. </div>
  7. <slot name="pullup" v-if="isloading" :pullUpLoad="pullUpLoad" :isPullUpLoad="isPullUpLoad">
  8. <div class="pullup-wrapper" v-if="pullUpLoad">
  9. <div class="before-trigger" v-if="!isPullUpLoad">
  10. <span>{{pullUpTxt}}</span>
  11. </div>
  12. <div class="after-trigger" v-else>
  13. <loading></loading>
  14. </div>
  15. </div>
  16. </slot>
  17. </div>
  18. <slot name="pulldown" :pullDownRefresh="pullDownRefresh" :pullDownStyle="pullDownStyle" :beforePullDown="beforePullDown" :isPullingDown="isPullingDown" :bubbleY="bubbleY" >
  19. <div ref="pulldown" class="pulldown-wrapper" :style="pullDownStyle" v-if="pullDownRefresh">
  20. <div class="before-trigger" v-if="beforePullDown">
  21. <bubble :y="bubbleY"></bubble>
  22. </div>
  23. <div class="after-trigger" v-else>
  24. <div v-if="isPullingDown" class="loading">
  25. <loading></loading>
  26. </div>
  27. <div v-else>
  28. <span>{{refreshTxt}}</span>
  29. </div>
  30. </div>
  31. </div>
  32. </slot>
  33. </div>
  34. </template>
  35. <script type="text/ecmascript-6">
  36. import BScroll from 'better-scroll'
  37. import Loading from '../loading/loading.vue'
  38. import Bubble from '../bubble/bubble.vue'
  39. import Dom from '../../util/dom'
  40. const COMPONENT_NAME = 'scroll'
  41. const DIRECTION_H = 'horizontal'
  42. const DIRECTION_V = 'vertical'
  43. export default {
  44. name: COMPONENT_NAME,
  45. props: {
  46. data: {
  47. type: Array,
  48. default: function () {
  49. return []
  50. }
  51. },
  52. probeType: {
  53. type: Number,
  54. default: 1
  55. },
  56. click: {
  57. type: Boolean,
  58. default: true
  59. },
  60. listenScroll: {
  61. type: Boolean,
  62. default: false
  63. },
  64. listenBeforeScroll: {
  65. type: Boolean,
  66. default: false
  67. },
  68. direction: {
  69. type: String,
  70. default: DIRECTION_V
  71. },
  72. scrollbar: {
  73. type: null,
  74. default: false
  75. },
  76. pullDownRefresh: {
  77. type: null,
  78. default: false
  79. },
  80. pullUpLoad: {
  81. type: null,
  82. default: false
  83. },
  84. startY: {
  85. type: Number,
  86. default: 0
  87. },
  88. refreshDelay: {
  89. type: Number,
  90. default: 20
  91. },
  92. freeScroll: {
  93. type: Boolean,
  94. default: false
  95. },
  96. mouseWheel: {
  97. type: Boolean,
  98. default: false
  99. },
  100. bounce: {
  101. default: false
  102. },
  103. isloading: {
  104. type: Boolean,
  105. default: false
  106. }
  107. },
  108. data () {
  109. return {
  110. beforePullDown: true,
  111. isRebounding: false,
  112. isPullingDown: false,
  113. isPullUpLoad: false,
  114. pullUpDirty: true,
  115. pullDownStyle: '',
  116. bubbleY: 0
  117. }
  118. },
  119. computed: {
  120. pullUpTxt () {
  121. const moreTxt = '--- 上拉更多 ---'
  122. const noMoreTxt = '--- 我也是有底线的 ---'
  123. return this.pullUpDirty ? moreTxt : noMoreTxt
  124. },
  125. refreshTxt () {
  126. return this.pullDownRefresh && (this.pullDownRefresh.txt || '已刷新')
  127. }
  128. },
  129. created () {
  130. this.pullDownInitTop = -50
  131. },
  132. mounted () {
  133. this.$nextTick(() => {
  134. this.initScroll()
  135. })
  136. },
  137. methods: {
  138. initScroll () {
  139. if (!this.$refs.wrapper) {
  140. return
  141. }
  142. if (this.$refs.listWrapper && (this.pullDownRefresh || this.pullUpLoad)) {
  143. this.$refs.listWrapper.style.minHeight = `${Dom.getRect(this.$refs.wrapper).height + 1}px`
  144. }
  145. let options = {
  146. probeType: this.probeType,
  147. click: this.click,
  148. scrollY: this.freeScroll || this.direction === DIRECTION_V,
  149. scrollX: this.freeScroll || this.direction === DIRECTION_H,
  150. scrollbar: this.scrollbar,
  151. pullDownRefresh: this.pullDownRefresh,
  152. pullUpLoad: this.pullUpLoad,
  153. startY: this.startY,
  154. freeScroll: this.freeScroll,
  155. mouseWheel: this.mouseWheel,
  156. bounce: this.bounce
  157. }
  158. this.scroll = new BScroll(this.$refs.wrapper, options)
  159. if (this.listenScroll) {
  160. this.scroll.on('scroll', (pos) => {
  161. this.$emit('scroll', pos)
  162. })
  163. }
  164. if (this.listenBeforeScroll) {
  165. this.scroll.on('beforeScrollStart', () => {
  166. this.$emit('beforeScrollStart')
  167. })
  168. }
  169. if (this.pullDownRefresh) {
  170. this._initPullDownRefresh()
  171. }
  172. if (this.pullUpLoad) {
  173. this._initPullUpLoad()
  174. }
  175. },
  176. disable () {
  177. this.scroll && this.scroll.disable()
  178. },
  179. enable () {
  180. this.scroll && this.scroll.enable()
  181. },
  182. refresh () {
  183. this.scroll && this.scroll.refresh()
  184. },
  185. scrollTo () {
  186. this.scroll && this.scroll.scrollTo.apply(this.scroll, arguments)
  187. },
  188. scrollToElement () {
  189. this.scroll && this.scroll.scrollToElement.apply(this.scroll, arguments)
  190. },
  191. clickItem (e, item) {
  192. this.$emit('click', item)
  193. },
  194. destroy () {
  195. this.scroll.destroy()
  196. },
  197. forceUpdate (dirty) {
  198. if (this.pullDownRefresh && this.isPullingDown) {
  199. this.isPullingDown = false
  200. this._reboundPullDown().then(() => {
  201. this._afterPullDown()
  202. })
  203. } else if (this.pullUpLoad && this.isPullUpLoad) {
  204. this.isPullUpLoad = false
  205. this.scroll.finishPullUp()
  206. this.pullUpDirty = dirty
  207. this.refresh()
  208. } else {
  209. this.refresh()
  210. }
  211. },
  212. _initPullDownRefresh () {
  213. this.scroll.on('pullingDown', () => {
  214. this.beforePullDown = false
  215. this.isPullingDown = true
  216. this.$emit('pullingDown')
  217. })
  218. this.scroll.on('scroll', (pos) => {
  219. if (!this.pullDownRefresh) {
  220. return
  221. }
  222. if (this.beforePullDown) {
  223. this.bubbleY = Math.max(0, pos.y + this.pullDownInitTop)
  224. this.pullDownStyle = `top:${Math.min(pos.y + this.pullDownInitTop, 10)}px`
  225. } else {
  226. this.bubbleY = 0
  227. }
  228. if (this.isRebounding) {
  229. this.pullDownStyle = `top:${10 - (this.pullDownRefresh.stop - pos.y)}px`
  230. }
  231. })
  232. },
  233. _initPullUpLoad () {
  234. this.scroll.on('pullingUp', () => {
  235. this.isPullUpLoad = true
  236. this.$emit('pullingUp')
  237. })
  238. },
  239. _reboundPullDown () {
  240. const { stopTime = 600 } = this.pullDownRefresh
  241. return new Promise((resolve) => {
  242. setTimeout(() => {
  243. this.isRebounding = true
  244. this.scroll.finishPullDown()
  245. resolve()
  246. }, stopTime)
  247. })
  248. },
  249. _afterPullDown () {
  250. setTimeout(() => {
  251. this.pullDownStyle = `top:${this.pullDownInitTop}px`
  252. this.beforePullDown = true
  253. this.isRebounding = false
  254. this.refresh()
  255. }, this.scroll.options.bounceTime)
  256. }
  257. },
  258. watch: {
  259. data () {
  260. setTimeout(() => {
  261. this.forceUpdate(true)
  262. }, this.refreshDelay)
  263. }
  264. },
  265. components: {
  266. Loading,
  267. Bubble
  268. }
  269. }
  270. </script>
  271. <style lang="scss" scoped>
  272. .list-wrapper {
  273. position: relative;
  274. height: 100%;
  275. overflow: hidden;
  276. background: #fff;
  277. }
  278. .scroll-content {
  279. position: relative;
  280. z-index: 1;
  281. }
  282. .list-content {
  283. position: relative;
  284. z-index: 10;
  285. background: #fff;
  286. }
  287. .pulldown-wrapper {
  288. position: absolute;
  289. width: 100%;
  290. left: 0;
  291. display: flex;
  292. justify-content: center;
  293. align-items: center;
  294. transition: all;
  295. }
  296. .after-trigger {
  297. margin-top: 10px;
  298. }
  299. .pullup-wrapper {
  300. width: 100%;
  301. display: flex;
  302. justify-content: center;
  303. align-items: center;
  304. padding-bottom: 10px;
  305. }
  306. </style>