load-refresh-bak.vue 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. <template>
  2. <view class="load-refresh">
  3. <!-- 刷新动画,可自定义,占高100rpx -->
  4. <view class="animation" :style="{'--color': color}">
  5. <view v-if="!playState" class="remind">
  6. {{moving ? '↑ 松开释放' : '↓ 下拉刷新'}}
  7. </view>
  8. <view v-if="playState && refreshType === 'hollowDots'" class="refresh hollow-dots-spinner">
  9. <view class="dot"></view>
  10. <view class="dot"></view>
  11. <view class="dot"></view>
  12. </view>
  13. <view v-if="playState && refreshType === 'halfCircle'" class="refresh half-circle-spinner">
  14. <view class="circle circle-1"></view>
  15. <view class="circle circle-2"></view>
  16. </view>
  17. <view v-if="playState && refreshType === 'swappingSquares'" class="refresh swapping-squares-spinner">
  18. <view class="square"></view>
  19. <view class="square"></view>
  20. <view class="square"></view>
  21. <view class="square"></view>
  22. </view>
  23. </view>
  24. <!-- 数据列表块 -->
  25. <view class="cover-container" :style="[{
  26. background: backgroundCover,
  27. transform: coverTransform,
  28. transition: coverTransition
  29. }]" @touchstart="coverTouchstart" @touchmove="coverTouchmove" @touchend="coverTouchend">
  30. <scroll-view scroll-y class="list" :scroll-top="scrollTop" :style="getHeight">
  31. <!-- 数据集插槽 -->
  32. <slot name="content-list"></slot>
  33. <!-- 上拉加载 -->
  34. <view class="load-more">{{loadText}}</view>
  35. </scroll-view>
  36. </view>
  37. </view>
  38. </template>
  39. <script>
  40. export default {
  41. name: 'loadRefresh',
  42. props: {
  43. refreshType: {
  44. type: String,
  45. default: 'hollowDots'
  46. },
  47. fixedHeight: {
  48. type: String,
  49. default: '0'
  50. },
  51. heightReduce: {
  52. type: String,
  53. default: '0'
  54. },
  55. color: {
  56. type: String,
  57. default: '#04C4C4'
  58. },
  59. backgroundCover: {
  60. type: String,
  61. default: 'white'
  62. },
  63. currentPage: {
  64. type: Number,
  65. default: 0
  66. },
  67. totalPages: {
  68. type: Number,
  69. default: 0
  70. }
  71. },
  72. data() {
  73. return {
  74. startY: 0,
  75. moveY: 0,
  76. isRefresh:true,
  77. updating: false, // 数据更新状态(true: 更新中)
  78. updateType: true, // 数据更新类型(true: 下拉刷新: false: 加载更多)
  79. moving: false,
  80. scrollTop: -1,
  81. coverTransform: 'translateY(0px)',
  82. coverTransition: '0s',
  83. playState: false // 动画的状态 暂停 paused/开始 running
  84. }
  85. },
  86. computed: {
  87. // 计算组件所占屏幕高度
  88. getHeight() {
  89. // rpx = px / uni.getSystemInfoSync().windowWidth * 750
  90. if (Number(this.fixedHeight)) {
  91. return `height: ${this.fixedHeight}rpx;`
  92. } else {
  93. let height = uni.getSystemInfoSync().windowHeight - uni.upx2px(0 + this.heightReduce) - uni.getSystemInfoSync().windowTop
  94. return `height: ${height}px;`
  95. }
  96. },
  97. // 判断loadText,可以根据需求自定义
  98. loadText() {
  99. const {
  100. currentPage,
  101. totalPages,
  102. updating,
  103. updateType
  104. } = this
  105. if (!updateType && updating) {
  106. return '加载中...'
  107. } else if (currentPage < totalPages) {
  108. return '上拉加载更多'
  109. } else {
  110. return '已经到底啦~'
  111. }
  112. }
  113. },
  114. onReachBottom:function(){
  115. debugger
  116. },
  117. methods: {
  118. // 根据currentPage和totalPages的值来判断 是否触发@loadMore
  119. loadMore() {
  120. const {
  121. currentPage,
  122. totalPages
  123. } = this
  124. if (!this.updating && currentPage < totalPages) {
  125. this.updating = true
  126. this.updateType = false
  127. this.$emit('loadMore')
  128. }
  129. },
  130. // 回弹效果
  131. coverTouchstart(e) {
  132. if (!this.isRefresh) {
  133. return
  134. }
  135. this.coverTransition = 'transform .1s linear'
  136. this.startY = e.touches[0].clientY
  137. },
  138. coverTouchmove(e) {
  139. if (!this.isRefresh || this.updating) {
  140. return
  141. }
  142. this.moveY = e.touches[0].clientY
  143. let moveDistance = this.moveY - this.startY
  144. // if (moveDistance <= 50) {
  145. // this.coverTransform = `translateY(${moveDistance}px)`
  146. // }
  147. if (moveDistance >= 50) {
  148. this.moving = true;
  149. this.isRefresh = true;
  150. this.updateType = true;
  151. this.coverTransform = `translateY(50px)`
  152. }else{
  153. this.moving = true;
  154. this.coverTransform = `translateY(${moveDistance}px)`
  155. }
  156. },
  157. coverTouchend() {
  158. // if (!this.isRefresh || this.updating) {
  159. // return
  160. // }
  161. if (this.updateType) {
  162. this.runRefresh()
  163. } else {
  164. this.coverTransition = 'transform 0.3s cubic-bezier(.21,1.93,.53,.64)'
  165. this.coverTransform = 'translateY(0px)'
  166. this.loadMore();
  167. }
  168. },
  169. runRefresh() {
  170. this.scrollTop = 0
  171. this.coverTransition = 'transform .1s linear'
  172. this.coverTransform = 'translateY(50px)'
  173. this.playState = true
  174. this.updating = true
  175. this.updateType = true
  176. this.$emit('refresh')
  177. },
  178. completed() {
  179. if (this.updateType) {
  180. // 下拉刷新
  181. this.moving = false
  182. this.scrollTop = -1
  183. this.coverTransition = 'transform 0.3s cubic-bezier(.21,1.93,.53,.64)'
  184. this.coverTransform = 'translateY(0px)'
  185. setTimeout(() => {
  186. this.playState = false
  187. }, 300)
  188. }
  189. this.updating = false
  190. }
  191. }
  192. }
  193. </script>
  194. <style lang="scss" scoped>
  195. $color: var(--color);
  196. .load-refresh {
  197. margin: 0;
  198. padding: 0;
  199. width: 100%;
  200. .cover-container {
  201. width: 100%;
  202. margin-top: -100rpx;
  203. .list {
  204. width: 100%;
  205. .load-more {
  206. font-size: 20rpx;
  207. text-align: center;
  208. color: #AAAAAA;
  209. padding: 16rpx;
  210. }
  211. }
  212. }
  213. }
  214. /* 动画 */
  215. .animation {
  216. width: 100%;
  217. height: 100rpx;
  218. .remind {
  219. width: 100%;
  220. height: 100rpx;
  221. text-align: center;
  222. line-height: 100rpx;
  223. }
  224. .refresh {
  225. width: 100%;
  226. height: 100rpx;
  227. display: flex;
  228. align-items: center;
  229. justify-content: center;
  230. box-sizing: border-box;
  231. view {
  232. // animation-play-state: $playState!important;
  233. }
  234. }
  235. /* HollowDots */
  236. .hollow-dots-spinner .dot {
  237. width: 30rpx;
  238. height: 30rpx;
  239. margin: 0 calc(30rpx / 2);
  240. border: calc(30rpx / 5) solid $color;
  241. border-radius: 50%;
  242. float: left;
  243. transform: scale(0);
  244. animation: hollowDots 1000ms ease infinite 0ms;
  245. }
  246. .hollow-dots-spinner .dot:nth-child(1) {
  247. animation-delay: calc(300ms * 1);
  248. }
  249. .hollow-dots-spinner .dot:nth-child(2) {
  250. animation-delay: calc(300ms * 2);
  251. }
  252. .hollow-dots-spinner .dot:nth-child(3) {
  253. animation-delay: calc(300ms * 3);
  254. }
  255. @keyframes hollowDots {
  256. 50% {
  257. transform: scale(1);
  258. opacity: 1;
  259. }
  260. 100% {
  261. opacity: 0;
  262. }
  263. }
  264. /* halfCircle */
  265. .half-circle-spinner .circle {
  266. content: "";
  267. position: absolute;
  268. width: 60rpx;
  269. height: 60rpx;
  270. border-radius: 100%;
  271. border: calc(60rpx / 10) solid transparent;
  272. }
  273. .half-circle-spinner .circle.circle-1 {
  274. border-top-color: $color;
  275. animation: halfCircle 1s infinite;
  276. }
  277. .half-circle-spinner .circle.circle-2 {
  278. border-bottom-color: $color;
  279. animation: halfCircle 1s infinite alternate;
  280. }
  281. @keyframes halfCircle {
  282. 0% {
  283. transform: rotate(0deg);
  284. }
  285. 100% {
  286. transform: rotate(360deg);
  287. }
  288. }
  289. /* swappingSquares */
  290. .swapping-squares-spinner {
  291. position: relative;
  292. }
  293. .swapping-squares-spinner .square {
  294. height: calc(65rpx * 0.25 / 1.3);
  295. width: calc(65rpx * 0.25 / 1.3);
  296. animation-duration: 1000ms;
  297. border: calc(65rpx * 0.04 / 1.3) solid $color;
  298. margin-right: auto;
  299. margin-left: auto;
  300. position: absolute;
  301. animation-iteration-count: infinite;
  302. }
  303. .swapping-squares-spinner .square:nth-child(1) {
  304. animation-name: swappingSquares-child-1;
  305. animation-delay: 500ms;
  306. }
  307. .swapping-squares-spinner .square:nth-child(2) {
  308. animation-name: swappingSquares-child-2;
  309. animation-delay: 0ms;
  310. }
  311. .swapping-squares-spinner .square:nth-child(3) {
  312. animation-name: swappingSquares-child-3;
  313. animation-delay: 500ms;
  314. }
  315. .swapping-squares-spinner .square:nth-child(4) {
  316. animation-name: swappingSquares-child-4;
  317. animation-delay: 0ms;
  318. }
  319. @keyframes swappingSquares-child-1 {
  320. 50% {
  321. transform: translate(150%, 150%) scale(2, 2);
  322. }
  323. }
  324. @keyframes swappingSquares-child-2 {
  325. 50% {
  326. transform: translate(-150%, 150%) scale(2, 2);
  327. }
  328. }
  329. @keyframes swappingSquares-child-3 {
  330. 50% {
  331. transform: translate(-150%, -150%) scale(2, 2);
  332. }
  333. }
  334. @keyframes swappingSquares-child-4 {
  335. 50% {
  336. transform: translate(150%, -150%) scale(2, 2);
  337. }
  338. }
  339. }
  340. </style>