zyq-movableRefresh.vue 6.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. <template>
  2. <view>
  3. <movable-area style="width: 100%;" :style="{'height':scrollHeight+50+'px'}">
  4. <view class="display_flex_center" style="height: 40px;font-size: 15px;color:#6b6a6a;">
  5. <view v-if="refreshSuccess">
  6. {{refreshSuccessText}}
  7. </view>
  8. <block v-else>
  9. <view v-if="!refreshing" class="refresh-icon display_flex_center" :class="{'refresh-icon-active': refreshFlag}">↓</view>
  10. <view v-if="!refreshing">{{refreshFlag?refreshReady:refreshText}}</view>
  11. <view v-else class="display_flex_center">
  12. <image src="./loading.gif" class="loading_img"></image>
  13. <text>{{refreshingText}}</text>
  14. </view>
  15. </block>
  16. </view>
  17. <movable-view :x="x" :y="y" :direction="isRefresh?'vertical':'none'" :out-of-bounds='false' style="width:100%;" :style="{'height':scrollHeight+'px'}"
  18. @change="onChange" @touchend="touchend">
  19. <view :style="{'height':scrollHeight+'px'}">
  20. <scroll-view :scroll-y="scrollStatus" :style="{'height':scrollHeight+'px','background':backgroundColor}" :scroll-into-view="top_viewId" :upper-threshold="10" @scroll="onScroll" @scrolltoupper="scrollToTop" @scrolltolower="loadMore">
  21. <view id="top_view" @touchstart="touchstart" @touchmove="touchMove">
  22. <slot></slot>
  23. </view>
  24. <view class="display_flex_center" style="height: 40px;font-size: 15px;color:#999">
  25. <block v-if="noMore">
  26. {{noMoreText}}
  27. </block>
  28. <block v-else-if="isLoading!=2">
  29. {{loadingMoreText}}
  30. </block>
  31. <block v-else>
  32. {{loadingText}}
  33. </block>
  34. </view>
  35. <view v-if="showGoTop && scrollTop>scrollHeight" class="go_top_icon_back" @click.stop="goTop">
  36. <image src="./go_top.png" class="top_icon"></image>
  37. </view>
  38. </scroll-view>
  39. </view>
  40. </movable-view>
  41. </movable-area>
  42. </view>
  43. </template>
  44. <script>
  45. export default {
  46. name:"zyq-movableRefresh",
  47. props: {
  48. isRefresh: {
  49. type: Boolean,
  50. default: true
  51. },
  52. scrollHeight: {
  53. type: Number,
  54. default: 300
  55. },
  56. refreshText: {
  57. type: String,
  58. default:'下拉可以刷新'
  59. },
  60. refreshReady: {
  61. type: String,
  62. default:'释放立即刷新'
  63. },
  64. refreshingText: {
  65. type: String,
  66. default:'正在刷新...'
  67. },
  68. refreshSuccessText: {
  69. type: String,
  70. default:'刷新完成'
  71. },
  72. loadingMoreText: {
  73. type: String,
  74. default:'上拉加载更多'
  75. },
  76. loadingText: {
  77. type: String,
  78. default:'正在加载...'
  79. },
  80. noMoreText: {
  81. type: String,
  82. default:'无更多数据'
  83. },
  84. pullHeight: {
  85. type: Number,
  86. default: 40
  87. },
  88. noMore: {
  89. type:Boolean,
  90. default:false
  91. },
  92. backgroundColor:{
  93. type: String,
  94. default:'#fff'
  95. },
  96. showGoTop: {
  97. type:Boolean,
  98. default:false
  99. }
  100. },
  101. data() {
  102. return {
  103. x: 50,
  104. y: 0,
  105. old: {
  106. x: 0,
  107. y: 0
  108. },
  109. refreshFlag: false,
  110. refreshing: false,
  111. scrollStatus: true,
  112. isLoading: 0, //加载状态,1:正在下拉刷新,2:正在触底加载
  113. refreshSuccess: false,
  114. scrollTop: 0,
  115. startX: 0,
  116. startY: 0,
  117. inTop: true,
  118. moveHeight: 0 ,//下拉刷新移动距离
  119. showLoadingMore: false,
  120. top_viewId: ''
  121. };
  122. },
  123. methods:{
  124. onChange(e){
  125. let y = e.detail.y
  126. this.old.x = e.detail.x
  127. this.old.y = y
  128. this.moveHeight = y
  129. if(y<this.pullHeight){
  130. this.refreshFlag = false
  131. }else{
  132. this.refreshFlag = true
  133. }
  134. },
  135. touchstart(e){
  136. let obj = e.changedTouches[0]
  137. this.startX = obj.pageX
  138. this.startY = obj.pageY
  139. },
  140. touchMove(e){
  141. if(!this.isRefresh){
  142. return
  143. }
  144. this.refreshSuccess = false
  145. let obj = e.changedTouches[0]
  146. let endX = obj.pageX
  147. let endY = obj.pageY
  148. let distanceX = endX - this.startX
  149. let distanceY = endY - this.startY
  150. if((this.inTop || this.scrollTop==0) && Math.abs(distanceX)<Math.abs(distanceY) && distanceY>0){
  151. this.scrollStatus = false
  152. }
  153. },
  154. touchend(e){
  155. let that = this
  156. let moveHeight = this.moveHeight
  157. if(moveHeight<this.pullHeight){
  158. this.x = this.old.x
  159. this.y = this.old.y
  160. this.$nextTick(function() {
  161. this.x = 0
  162. this.y = 0
  163. })
  164. this.refreshFlag = false
  165. this.refreshing = false
  166. }else{
  167. this.refreshing = true
  168. this.refreshFlag = true
  169. this.refresh()
  170. }
  171. this.scrollStatus = true
  172. },
  173. scrollToTop(e){
  174. if(this.scrollTop<5){
  175. this.inTop = true
  176. }
  177. },
  178. onScroll(e){
  179. let scrollTop = e.detail.scrollTop
  180. this.scrollTop = scrollTop
  181. this.$emit('onScroll',scrollTop);
  182. if(scrollTop>3){
  183. this.inTop = false
  184. }else{
  185. this.inTop = true
  186. }
  187. },
  188. goTop(){
  189. this.top_viewId = ""
  190. this.$nextTick(function() {
  191. this.top_viewId = "top_view"
  192. })
  193. },
  194. runRefresh(callback=false){
  195. let that = this
  196. this.x = this.old.x
  197. this.y = this.old.y
  198. this.$nextTick(function() {
  199. this.scrollTop = 0
  200. this.x = that.pullHeight
  201. this.y = that.pullHeight
  202. that.refreshing = true
  203. that.refreshFlag = true
  204. that.refresh()
  205. })
  206. },
  207. refresh(){
  208. if(this.isLoading){
  209. return
  210. }
  211. this.isLoading = 1
  212. this.$emit('refresh');
  213. },
  214. loadMore(){
  215. if(this.noMore || this.isLoading){
  216. return
  217. }
  218. this.isLoading = 2
  219. this.$emit('loadMore');
  220. },
  221. endLoad(){
  222. let that = this
  223. this.refreshSuccess = true
  224. setTimeout(function(){
  225. that.refreshSuccess = false
  226. },800)
  227. this.x = this.old.x
  228. this.y = this.old.y
  229. this.$nextTick(function() {
  230. this.x = 0
  231. this.y = 0
  232. })
  233. this.scrollStatus = true
  234. this.refreshing = false
  235. this.refreshFlag = false
  236. this.isLoading = 0
  237. }
  238. }
  239. }
  240. </script>
  241. <style>
  242. .display_flex_center{
  243. display: flex;
  244. justify-content: center;
  245. align-items: center;
  246. }
  247. .refresh-icon {
  248. width: 20px;
  249. height: 20px;
  250. font-weight: 700;
  251. transition-duration: .5s;
  252. transition-property: transform;
  253. transform: rotate(0deg);
  254. }
  255. .refresh-icon-active {
  256. transform: rotate(180deg);
  257. }
  258. .loading_img{
  259. width: 20px;
  260. height: 20px;
  261. margin-right: 5px;
  262. }
  263. .go_top_icon_back{
  264. position: fixed;
  265. right: 40px;
  266. bottom: 40px;
  267. height: 40px;
  268. width: 40px;
  269. border-radius: 50%;
  270. background: #fff;
  271. display: flex;
  272. justify-content: center;
  273. align-items: center;
  274. }
  275. .top_icon{
  276. height: 30px;
  277. width: 30px;
  278. }
  279. </style>