You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

336 lines
8.2 KiB

3 years ago
<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" @scrolltolower="loadMore" :style="getHeight">
<!-- 数据集插槽 -->
<slot name="content-list"></slot>
<!-- 上拉加载 -->
<view class="load-more">{{loadText}}</view>
</scroll-view>
</view>
</view>
</template>
<script>
export default {
name: 'loadRefresh',
props: {
isRefresh: {
type: Boolean,
default: true
},
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,
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)
return `height: ${height}px;`
}
},
// 判断loadText可以根据需求自定义
loadText() {
const { currentPage, totalPages, updating, updateType } = this
if (!updateType && updating) {
return '加载中...'
} else if (currentPage < totalPages) {
return '上拉加载更多'
} else {
return '已经到底啦~'
}
}
},
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)`
}
this.moving = moveDistance >= 50
},
coverTouchend() {
if (!this.isRefresh || this.updating) {
return
}
if (this.moving) {
this.runRefresh()
} else {
this.coverTransition = 'transform 0.3s cubic-bezier(.21,1.93,.53,.64)'
this.coverTransform = 'translateY(0px)'
}
},
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>