123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371 |
- <template>
- <view class="load-refresh">
- <!-- 刷新动画,可自定义,占高100rpx -->
- <view class="animation" :style="{'--color': color}">
- <view v-if="!playState" class="remind">
- {{moving ? '↑ 松开释放' : '↓ 下拉刷新'}}
- </view>
- <view v-if="playState && refreshType === 'hollowDots'" class="refresh hollow-dots-spinner">
- <view class="dot"></view>
- <view class="dot"></view>
- <view class="dot"></view>
- </view>
- <view v-if="playState && refreshType === 'halfCircle'" class="refresh half-circle-spinner">
- <view class="circle circle-1"></view>
- <view class="circle circle-2"></view>
- </view>
- <view v-if="playState && refreshType === 'swappingSquares'" class="refresh swapping-squares-spinner">
- <view class="square"></view>
- <view class="square"></view>
- <view class="square"></view>
- <view class="square"></view>
- </view>
- </view>
- <!-- 数据列表块 -->
- <view class="cover-container" :style="[{
- background: backgroundCover,
- transform: coverTransform,
- transition: coverTransition
- }]" @touchstart="coverTouchstart" @touchmove="coverTouchmove" @touchend="coverTouchend">
- <scroll-view scroll-y class="list" :scroll-top="scrollTop" :style="getHeight">
- <!-- 数据集插槽 -->
- <slot name="content-list"></slot>
- <!-- 上拉加载 -->
- <view class="load-more">{{loadText}}</view>
- </scroll-view>
- </view>
- </view>
- </template>
- <script>
- export default {
- name: 'loadRefresh',
- props: {
- refreshType: {
- type: String,
- default: 'hollowDots'
- },
- fixedHeight: {
- type: String,
- default: '0'
- },
- heightReduce: {
- type: String,
- default: '0'
- },
- color: {
- type: String,
- default: '#04C4C4'
- },
- backgroundCover: {
- type: String,
- default: 'white'
- },
- currentPage: {
- type: Number,
- default: 0
- },
- totalPages: {
- type: Number,
- default: 0
- }
- },
- data() {
- return {
- startY: 0,
- moveY: 0,
- isRefresh:true,
- updating: false, // 数据更新状态(true: 更新中)
- updateType: true, // 数据更新类型(true: 下拉刷新: false: 加载更多)
- moving: false,
- scrollTop: -1,
- coverTransform: 'translateY(0px)',
- coverTransition: '0s',
- playState: false // 动画的状态 暂停 paused/开始 running
- }
- },
- computed: {
- // 计算组件所占屏幕高度
- getHeight() {
- // rpx = px / uni.getSystemInfoSync().windowWidth * 750
- if (Number(this.fixedHeight)) {
- return `height: ${this.fixedHeight}rpx;`
- } else {
- let height = uni.getSystemInfoSync().windowHeight - uni.upx2px(0 + this.heightReduce) - uni.getSystemInfoSync().windowTop
- return `height: ${height}px;`
- }
- },
- // 判断loadText,可以根据需求自定义
- loadText() {
- const {
- currentPage,
- totalPages,
- updating,
- updateType
- } = this
- if (!updateType && updating) {
- return '加载中...'
- } else if (currentPage < totalPages) {
- return '上拉加载更多'
- } else {
- return '已经到底啦~'
- }
- }
- },
- onReachBottom:function(){
- debugger
- },
- methods: {
- // 根据currentPage和totalPages的值来判断 是否触发@loadMore
- loadMore() {
- const {
- currentPage,
- totalPages
- } = this
- if (!this.updating && currentPage < totalPages) {
- this.updating = true
- this.updateType = false
- this.$emit('loadMore')
- }
- },
- // 回弹效果
- coverTouchstart(e) {
- if (!this.isRefresh) {
- return
- }
- this.coverTransition = 'transform .1s linear'
- this.startY = e.touches[0].clientY
- },
- coverTouchmove(e) {
- if (!this.isRefresh || this.updating) {
- return
- }
- this.moveY = e.touches[0].clientY
- let moveDistance = this.moveY - this.startY
- // if (moveDistance <= 50) {
- // this.coverTransform = `translateY(${moveDistance}px)`
- // }
- if (moveDistance >= 50) {
- this.moving = true;
- this.isRefresh = true;
- this.updateType = true;
- this.coverTransform = `translateY(50px)`
- }else{
- this.moving = true;
- this.coverTransform = `translateY(${moveDistance}px)`
- }
- },
- coverTouchend() {
- // if (!this.isRefresh || this.updating) {
- // return
- // }
- if (this.updateType) {
- this.runRefresh()
- } else {
- this.coverTransition = 'transform 0.3s cubic-bezier(.21,1.93,.53,.64)'
- this.coverTransform = 'translateY(0px)'
- this.loadMore();
- }
- },
- runRefresh() {
- this.scrollTop = 0
- this.coverTransition = 'transform .1s linear'
- this.coverTransform = 'translateY(50px)'
- this.playState = true
- this.updating = true
- this.updateType = true
- this.$emit('refresh')
- },
- completed() {
- if (this.updateType) {
- // 下拉刷新
- this.moving = false
- this.scrollTop = -1
- this.coverTransition = 'transform 0.3s cubic-bezier(.21,1.93,.53,.64)'
- this.coverTransform = 'translateY(0px)'
- setTimeout(() => {
- this.playState = false
- }, 300)
- }
- this.updating = false
- }
- }
- }
- </script>
- <style lang="scss" scoped>
- $color: var(--color);
- .load-refresh {
- margin: 0;
- padding: 0;
- width: 100%;
- .cover-container {
- width: 100%;
- margin-top: -100rpx;
- .list {
- width: 100%;
- .load-more {
- font-size: 20rpx;
- text-align: center;
- color: #AAAAAA;
- padding: 16rpx;
- }
- }
- }
- }
- /* 动画 */
- .animation {
- width: 100%;
- height: 100rpx;
- .remind {
- width: 100%;
- height: 100rpx;
- text-align: center;
- line-height: 100rpx;
- }
- .refresh {
- width: 100%;
- height: 100rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- box-sizing: border-box;
- view {
- // animation-play-state: $playState!important;
- }
- }
- /* HollowDots */
- .hollow-dots-spinner .dot {
- width: 30rpx;
- height: 30rpx;
- margin: 0 calc(30rpx / 2);
- border: calc(30rpx / 5) solid $color;
- border-radius: 50%;
- float: left;
- transform: scale(0);
- animation: hollowDots 1000ms ease infinite 0ms;
- }
- .hollow-dots-spinner .dot:nth-child(1) {
- animation-delay: calc(300ms * 1);
- }
- .hollow-dots-spinner .dot:nth-child(2) {
- animation-delay: calc(300ms * 2);
- }
- .hollow-dots-spinner .dot:nth-child(3) {
- animation-delay: calc(300ms * 3);
- }
- @keyframes hollowDots {
- 50% {
- transform: scale(1);
- opacity: 1;
- }
- 100% {
- opacity: 0;
- }
- }
- /* halfCircle */
- .half-circle-spinner .circle {
- content: "";
- position: absolute;
- width: 60rpx;
- height: 60rpx;
- border-radius: 100%;
- border: calc(60rpx / 10) solid transparent;
- }
- .half-circle-spinner .circle.circle-1 {
- border-top-color: $color;
- animation: halfCircle 1s infinite;
- }
- .half-circle-spinner .circle.circle-2 {
- border-bottom-color: $color;
- animation: halfCircle 1s infinite alternate;
- }
- @keyframes halfCircle {
- 0% {
- transform: rotate(0deg);
- }
- 100% {
- transform: rotate(360deg);
- }
- }
- /* swappingSquares */
- .swapping-squares-spinner {
- position: relative;
- }
- .swapping-squares-spinner .square {
- height: calc(65rpx * 0.25 / 1.3);
- width: calc(65rpx * 0.25 / 1.3);
- animation-duration: 1000ms;
- border: calc(65rpx * 0.04 / 1.3) solid $color;
- margin-right: auto;
- margin-left: auto;
- position: absolute;
- animation-iteration-count: infinite;
- }
- .swapping-squares-spinner .square:nth-child(1) {
- animation-name: swappingSquares-child-1;
- animation-delay: 500ms;
- }
- .swapping-squares-spinner .square:nth-child(2) {
- animation-name: swappingSquares-child-2;
- animation-delay: 0ms;
- }
- .swapping-squares-spinner .square:nth-child(3) {
- animation-name: swappingSquares-child-3;
- animation-delay: 500ms;
- }
- .swapping-squares-spinner .square:nth-child(4) {
- animation-name: swappingSquares-child-4;
- animation-delay: 0ms;
- }
- @keyframes swappingSquares-child-1 {
- 50% {
- transform: translate(150%, 150%) scale(2, 2);
- }
- }
- @keyframes swappingSquares-child-2 {
- 50% {
- transform: translate(-150%, 150%) scale(2, 2);
- }
- }
- @keyframes swappingSquares-child-3 {
- 50% {
- transform: translate(-150%, -150%) scale(2, 2);
- }
- }
- @keyframes swappingSquares-child-4 {
- 50% {
- transform: translate(150%, -150%) scale(2, 2);
- }
- }
- }
- </style>
|