626 lines
17 KiB
Vue
626 lines
17 KiB
Vue
|
<template>
|
|||
|
<view class="ccPullScroll" :class="customClass" :style="{height}">
|
|||
|
|
|||
|
<!-- 欢迎关注“前端组件开发”公众号 -->
|
|||
|
<scroll-view :id="scrollId" class="ccPullScrollview" :scroll-top="scrollTop" :scroll-with-animation="false"
|
|||
|
:scroll-y="scrollAble" :enable-back-to-top="true" @scroll="scroll" @touchstart="touchstart"
|
|||
|
@touchmove="touchmove" @touchend="touchend" :lower-threshold="upOffset" @touchcancel="touchend">
|
|||
|
<view :style="{'transform': translateY, 'transition': transition}">
|
|||
|
<view class="cc-pull-down-wrap"
|
|||
|
:class="[{'is-success': isShowDownTip && isDownSuccess},{'is-error': isShowDownTip && isDownError}]"
|
|||
|
:style="{'height':downOffset+'rpx'}">
|
|||
|
<view class="cc-pull-loading-icon" v-if="!isShowDownTip"
|
|||
|
:class="{'cc-pull-loading-rotate':isDownLoading}" :style="{'transform':downRotate}"></view>
|
|||
|
<view>{{downText}}</view>
|
|||
|
</view>
|
|||
|
|
|||
|
<slot></slot>
|
|||
|
|
|||
|
<slot name="empty" v-if="isEmpty"></slot>
|
|||
|
|
|||
|
<view class="cc-pull-up-wrap">
|
|||
|
<slot name="up-loading" v-if="isUpLoading">
|
|||
|
<view class="cc-pull-loading-icon cc-pull-loading-rotate"></view>
|
|||
|
<view>{{upLoadingText}}</view>
|
|||
|
</slot>
|
|||
|
<slot name="up-error" v-if="isUpError && showUpError">
|
|||
|
<view v-if="upErrorText" @click="onUpErrorClick">{{upErrorText}}</view>
|
|||
|
</slot>
|
|||
|
<slot name="up-finish" v-else-if="isUpFinish && showUpFinish">
|
|||
|
<view v-if="upFinishText">{{upFinishText}}</view>
|
|||
|
</slot>
|
|||
|
</view>
|
|||
|
</view>
|
|||
|
</scroll-view>
|
|||
|
<!-- 回到顶部按钮 -->
|
|||
|
<view class="cc-pull-back-top" v-if="backTop" :class="{'is-show':isShowBackTop}" @click="onBackTop">
|
|||
|
<slot name="backtop">
|
|||
|
<image class="default-back-top" :src="defaultBackTopImgSrc" mode="aspectFill" />
|
|||
|
</slot>
|
|||
|
</view>
|
|||
|
</view>
|
|||
|
</template>
|
|||
|
|
|||
|
<script>
|
|||
|
// #ifndef VUE3
|
|||
|
const defaultBackTopImgSrc = require('./back-top.png');
|
|||
|
// #endif
|
|||
|
|
|||
|
// #ifdef VUE3
|
|||
|
// const defaultBackTopImgSrc = new URL('./back-top.png', import.meta.url).href;
|
|||
|
// 适配小程序
|
|||
|
import defaultBackTopImgSrc from './back-top.png';
|
|||
|
// #endif
|
|||
|
|
|||
|
export default {
|
|||
|
name: 'ccPullScroll',
|
|||
|
data() {
|
|||
|
Object.assign(this, {
|
|||
|
pullType: '',
|
|||
|
scrollRealTop: 0, // 滚动条的位置
|
|||
|
scrollHeight: 0,
|
|||
|
page: 1,
|
|||
|
startPoint: null,
|
|||
|
lastPoint: null,
|
|||
|
startTop: 0,
|
|||
|
inTouchend: false,
|
|||
|
movetype: 0,
|
|||
|
startAngle: 0,
|
|||
|
isMoveDown: false
|
|||
|
});
|
|||
|
return {
|
|||
|
scrollId: 'ccPullScrollview-id-' + Math.random().toString(36).substr(2), // 随机生成mescroll的id(不能数字开头,否则找不到元素)
|
|||
|
defaultBackTopImgSrc,
|
|||
|
downHight: 0, // 下拉刷新: 容器高度
|
|||
|
downRotate: 0, // 下拉刷新: 圆形进度条旋转的角度
|
|||
|
downText: '', // 下拉刷新: 提示的文本
|
|||
|
isEmpty: false, // 是否显示空布局
|
|||
|
isShowDownTip: false, // 下拉刷新提示结果
|
|||
|
isDownSuccess: false, // 下拉刷新成功
|
|||
|
isDownError: false, // 下拉刷新失败
|
|||
|
isDownReset: false, // 下拉刷新: 是否显示重置的过渡动画
|
|||
|
isDownLoading: false, // 下拉刷新: 是否显示加载中
|
|||
|
isUpLoading: false, // 上拉加载: 是否显示 "加载中..."
|
|||
|
isUpFinish: false, // 是否加载完毕
|
|||
|
isUpError: false, // 是否上拉加载出错
|
|||
|
isShowBackTop: false, // 是否显示回到顶部按钮
|
|||
|
scrollAble: true, // 是否禁止下滑 (下拉时禁止,避免抖动)
|
|||
|
scrollTop: 0 // 滚动条的位置
|
|||
|
};
|
|||
|
},
|
|||
|
props: {
|
|||
|
// class
|
|||
|
customClass: {
|
|||
|
type: String,
|
|||
|
default: ''
|
|||
|
},
|
|||
|
// 设置高度,默认继承父级高度
|
|||
|
height: {
|
|||
|
default: '100%'
|
|||
|
},
|
|||
|
// 下拉时文案
|
|||
|
pullingText: {
|
|||
|
type: String,
|
|||
|
default: '下拉刷新'
|
|||
|
},
|
|||
|
// 下拉释放时文案
|
|||
|
loosingText: {
|
|||
|
type: String,
|
|||
|
default: '释放刷新'
|
|||
|
},
|
|||
|
// 下拉释放后文案
|
|||
|
downLoadingText: {
|
|||
|
type: String,
|
|||
|
default: '正在刷新 ...'
|
|||
|
},
|
|||
|
// 上拉加载时文案
|
|||
|
upLoadingText: {
|
|||
|
type: String,
|
|||
|
default: '加载中 ...'
|
|||
|
},
|
|||
|
// 是否显示下拉刷新成功
|
|||
|
showDownSuccess: {
|
|||
|
type: Boolean,
|
|||
|
default: false
|
|||
|
},
|
|||
|
// 下拉刷新成功文案
|
|||
|
downSuccessText: {
|
|||
|
type: String,
|
|||
|
default: '刷新成功'
|
|||
|
},
|
|||
|
// 是否显示下拉刷新失败
|
|||
|
showDownError: {
|
|||
|
type: Boolean,
|
|||
|
default: false
|
|||
|
},
|
|||
|
// 下拉刷新失败文案
|
|||
|
downErrorText: {
|
|||
|
type: String,
|
|||
|
default: '刷新失败'
|
|||
|
},
|
|||
|
// 是否显示上拉加载时失败
|
|||
|
showUpError: {
|
|||
|
type: Boolean,
|
|||
|
default: true
|
|||
|
},
|
|||
|
// 上拉加载失败文案
|
|||
|
upErrorText: {
|
|||
|
type: String,
|
|||
|
default: '加载失败,点击重新加载'
|
|||
|
},
|
|||
|
// 是否显示上拉加载数据全部完成
|
|||
|
showUpFinish: {
|
|||
|
type: Boolean,
|
|||
|
default: true
|
|||
|
},
|
|||
|
// 上拉加载完毕文案
|
|||
|
upFinishText: {
|
|||
|
type: String,
|
|||
|
default: '暂无更多了'
|
|||
|
},
|
|||
|
// 下拉配置
|
|||
|
// 下拉回掉,参数为vm
|
|||
|
pullDown: Function,
|
|||
|
// 是否允许下拉刷新
|
|||
|
enablePullDown: {
|
|||
|
type: Boolean,
|
|||
|
default: true
|
|||
|
},
|
|||
|
downOffset: {
|
|||
|
type: Number,
|
|||
|
default: 100
|
|||
|
},
|
|||
|
downMinAngle: {
|
|||
|
type: Number,
|
|||
|
default: 45
|
|||
|
},
|
|||
|
downInOffsetRate: {
|
|||
|
type: Number,
|
|||
|
default: 1
|
|||
|
},
|
|||
|
downOutOffsetRate: {
|
|||
|
type: Number,
|
|||
|
default: 0.2
|
|||
|
},
|
|||
|
downStartTop: {
|
|||
|
type: Number,
|
|||
|
default: 100
|
|||
|
},
|
|||
|
// 下拉释放失效高度
|
|||
|
downTouchHeight: {
|
|||
|
type: Number,
|
|||
|
default: 1200
|
|||
|
},
|
|||
|
|
|||
|
|
|||
|
upOffset: {
|
|||
|
type: Number,
|
|||
|
default: 100
|
|||
|
},
|
|||
|
// 回到顶部
|
|||
|
backTop: Boolean,
|
|||
|
// 滚动距离大于多少rpx时触发
|
|||
|
backTopOffset: {
|
|||
|
type: Number,
|
|||
|
default: 1000
|
|||
|
}
|
|||
|
},
|
|||
|
computed: {
|
|||
|
numBackTopOffset() {
|
|||
|
return uni.upx2px(this.backTopOffset);
|
|||
|
},
|
|||
|
numDownStartTop() {
|
|||
|
return uni.upx2px(this.downStartTop);
|
|||
|
},
|
|||
|
numDownOffset() {
|
|||
|
return uni.upx2px(this.downOffset);
|
|||
|
},
|
|||
|
numDownTouchHeight() {
|
|||
|
return uni.upx2px(this.downTouchHeight);
|
|||
|
},
|
|||
|
transition() {
|
|||
|
return this.isDownReset ? 'transform 300ms' : '';
|
|||
|
},
|
|||
|
translateY() {
|
|||
|
return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' : '';
|
|||
|
}
|
|||
|
},
|
|||
|
methods: {
|
|||
|
// 注册列表滚动事件,用于下拉刷新
|
|||
|
scroll(e) {
|
|||
|
e = e.detail;
|
|||
|
// 更新滚动条的位置
|
|||
|
this.scrollRealTop = e.scrollTop;
|
|||
|
// 更新滚动内容高度
|
|||
|
this.scrollHeight = e.scrollHeight;
|
|||
|
// 回到顶部功能
|
|||
|
if (this.backTop) {
|
|||
|
// 返回顶部按钮的显示隐藏
|
|||
|
if (e.scrollTop >= this.numBackTopOffset) {
|
|||
|
this.isShowBackTop = true;
|
|||
|
} else {
|
|||
|
this.isShowBackTop = false;
|
|||
|
}
|
|||
|
}
|
|||
|
},
|
|||
|
// 注册列表touchstart事件,用于下拉刷新
|
|||
|
touchstart(e) {
|
|||
|
if (!this.pullDown || !this.enablePullDown) return;
|
|||
|
this.startPoint = this.getPoint(e); // 记录起点
|
|||
|
this.startTop = this.scrollRealTop; // 记录此时的滚动条位置
|
|||
|
this.startAngle = 0; // 初始角度
|
|||
|
this.lastPoint = this.startPoint; // 重置上次move的点
|
|||
|
this.inTouchend = false; // 标记不是touchend
|
|||
|
},
|
|||
|
// 注册列表touchmove事件,用于下拉刷新
|
|||
|
touchmove(e) {
|
|||
|
if (!this.pullDown || !this.enablePullDown) return;
|
|||
|
|
|||
|
const scrollTop = this.scrollRealTop; // 当前滚动条的距离
|
|||
|
const curPoint = this.getPoint(e); // 当前点
|
|||
|
|
|||
|
const moveY = curPoint.y - this.startPoint.y; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
|
|||
|
|
|||
|
// (向下拉&&在顶部) scroll-view在滚动时不会触发touchmove,当触顶/底/左/右时,才会触发touchmove
|
|||
|
// scroll-view滚动到顶部时,scrollTop不一定为0; 在iOS的APP中scrollTop可能为负数,不一定和startTop相等
|
|||
|
if (moveY > 0 && (scrollTop <= 0 || (scrollTop <= this.numDownStartTop && scrollTop === this.startTop))) {
|
|||
|
// 可下拉的条件
|
|||
|
if (this.pullDown && this.enablePullDown && !this.inTouchend && !this.isDownLoading && !this
|
|||
|
.isUpLoading) {
|
|||
|
// 下拉的初始角度是否在配置的范围内
|
|||
|
if (!this.startAngle) this.startAngle = this.getAngle(this.lastPoint,
|
|||
|
curPoint); // 两点之间的角度,区间 [0,90]
|
|||
|
if (this.startAngle < this.downMinAngle) return; // 如果小于配置的角度,则不往下执行下拉刷新
|
|||
|
|
|||
|
// 如果手指的位置超过配置的距离,则提前结束下拉,避免Webview嵌套导致touchend无法触发
|
|||
|
if (this.numDownTouchHeight > 0 && curPoint.y >= this.numDownTouchHeight) {
|
|||
|
this.inTouchend = true; // 标记执行touchend
|
|||
|
this.touchend(); // 提前触发touchend
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
this.preventDefault(e); // 阻止默认事件
|
|||
|
|
|||
|
const diff = curPoint.y - this.lastPoint.y; // 和上次比,移动的距离 (大于0向下,小于0向上)
|
|||
|
|
|||
|
// 下拉距离 < 指定距离
|
|||
|
if (this.downHight < this.numDownOffset) {
|
|||
|
if (this.movetype !== 1) {
|
|||
|
this.movetype = 1; // 加入标记,保证只执行一次
|
|||
|
// 下拉的距离进入offset范围内那一刻的回调
|
|||
|
this.scrollAble = false; // 禁止下拉,避免抖动
|
|||
|
this.isDownReset = false; // 不重置高度
|
|||
|
this.isDownLoading = false; // 不显示加载中
|
|||
|
this.downText = this.pullingText; // 设置文本
|
|||
|
this.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
|
|||
|
}
|
|||
|
this.downHight += diff * this.downInOffsetRate; // 越往下,高度变化越小
|
|||
|
// 指定距离 <= 下拉距离
|
|||
|
} else {
|
|||
|
if (this.movetype !== 2) {
|
|||
|
this.movetype = 2; // 加入标记,保证只执行一次
|
|||
|
// 下拉的距离大于offset那一刻的回调
|
|||
|
this.scrollAble = false; // 禁止下拉,避免抖动
|
|||
|
this.isDownReset = false; // 不重置高度
|
|||
|
this.isDownLoading = false; // 不显示加载中
|
|||
|
this.downText = this.loosingText; // 设置文本
|
|||
|
this.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
|
|||
|
}
|
|||
|
if (diff > 0) { // 向下拉
|
|||
|
this.downHight += Math.round(diff * this.downOutOffsetRate); // 越往下,高度变化越小
|
|||
|
} else { // 向上收
|
|||
|
this.downHight += diff; // 向上收回高度,则向上滑多少收多少高度
|
|||
|
}
|
|||
|
}
|
|||
|
// 设置旋转角度
|
|||
|
this.downRotate = 'rotate(' + 360 * (this.downHight / this.numDownOffset) + 'deg)';
|
|||
|
}
|
|||
|
}
|
|||
|
// 记录本次移动的点
|
|||
|
this.lastPoint = curPoint;
|
|||
|
},
|
|||
|
// 注册列表touchend事件,用于下拉刷新
|
|||
|
touchend(e) {
|
|||
|
if (!this.pullDown || !this.enablePullDown) return;
|
|||
|
// 如果下拉区域高度已改变,则需重置回来
|
|||
|
if (this.isMoveDown) {
|
|||
|
if (this.downHight >= this.numDownOffset) {
|
|||
|
// 符合触发刷新的条件
|
|||
|
this.triggerPullDown();
|
|||
|
} else {
|
|||
|
// 不符合的话 则重置
|
|||
|
this.downHight = 0;
|
|||
|
this.scrollAble = true; // 开启下拉
|
|||
|
this.isDownReset = true; // 重置高度
|
|||
|
this.isDownLoading = false; // 不显示加载中
|
|||
|
this.scrollTo(0);
|
|||
|
}
|
|||
|
this.movetype = 0;
|
|||
|
this.isMoveDown = false;
|
|||
|
}
|
|||
|
},
|
|||
|
/* 计算两点之间的角度: 区间 [0,90] */
|
|||
|
getAngle(p1, p2) {
|
|||
|
const x = Math.abs(p1.x - p2.x);
|
|||
|
const y = Math.abs(p1.y - p2.y);
|
|||
|
const z = Math.sqrt(x * x + y * y);
|
|||
|
let angle = 0;
|
|||
|
if (z !== 0) {
|
|||
|
angle = Math.asin(y / z) / Math.PI * 180;
|
|||
|
}
|
|||
|
return angle;
|
|||
|
},
|
|||
|
preventDefault(e) {
|
|||
|
// 小程序不支持e.preventDefault
|
|||
|
// app的bounce只能通过配置pages.json的style.app-plus.bounce为"none"来禁止
|
|||
|
// cancelable:是否可以被禁用; defaultPrevented:是否已经被禁用
|
|||
|
if (e && e.cancelable && !e.defaultPrevented) e.preventDefault();
|
|||
|
},
|
|||
|
// 点击回到顶部的按钮回调
|
|||
|
onBackTop() {
|
|||
|
this.isShowBackTop = false; // 回到顶部按钮需要先隐藏,再执行回到顶部,避免闪动
|
|||
|
this.scrollTo(0); // 执行回到顶部
|
|||
|
},
|
|||
|
// 点击失败重新加载
|
|||
|
onUpErrorClick() {
|
|||
|
this.isUpError = false;
|
|||
|
this.triggerPullDown();
|
|||
|
},
|
|||
|
scrollTo(y) {
|
|||
|
this.scrollTop = this.scrollRealTop;
|
|||
|
this.$nextTick(() => {
|
|||
|
this.scrollTop = y;
|
|||
|
});
|
|||
|
},
|
|||
|
/* 根据点击滑动事件获取第一个手指的坐标 */
|
|||
|
getPoint(e) {
|
|||
|
if (!e) {
|
|||
|
return {
|
|||
|
x: 0,
|
|||
|
y: 0
|
|||
|
};
|
|||
|
}
|
|||
|
if (e.touches && e.touches[0]) {
|
|||
|
return {
|
|||
|
x: e.touches[0].pageX,
|
|||
|
y: e.touches[0].pageY
|
|||
|
};
|
|||
|
} else if (e.changedTouches && e.changedTouches[0]) {
|
|||
|
return {
|
|||
|
x: e.changedTouches[0].pageX,
|
|||
|
y: e.changedTouches[0].pageY
|
|||
|
};
|
|||
|
} else {
|
|||
|
return {
|
|||
|
x: e.clientX,
|
|||
|
y: e.clientY
|
|||
|
};
|
|||
|
}
|
|||
|
},
|
|||
|
/* 显示上拉加载中 */
|
|||
|
showUpLoading() {
|
|||
|
this.isEmpty = false;
|
|||
|
this.isUpError = false;
|
|||
|
this.isUpFinish = false;
|
|||
|
this.isUpLoading = true;
|
|||
|
},
|
|||
|
/* 显示下拉进度布局 */
|
|||
|
showDownLoading() {
|
|||
|
this.isEmpty = false;
|
|||
|
this.isUpLoading = false;
|
|||
|
this.isUpError = false;
|
|||
|
this.isUpFinish = false;
|
|||
|
|
|||
|
this.isShowDownTip = false;
|
|||
|
this.isDownSuccess = false;
|
|||
|
this.isDownError = false;
|
|||
|
this.isDownLoading = true; // 显示加载中
|
|||
|
this.downHight = this.numDownOffset; // 更新下拉区域高度
|
|||
|
this.scrollAble = true; // 开启下拉
|
|||
|
this.isDownReset = true; // 重置高度
|
|||
|
this.downText = this.downLoadingText; // 设置文本
|
|||
|
},
|
|||
|
/* 结束下拉刷新 */
|
|||
|
hideDownLoading() {
|
|||
|
if (this.isDownLoading) {
|
|||
|
if (this.isDownSuccess && this.showDownSuccess) {
|
|||
|
this.downText = this.downSuccessText;
|
|||
|
this.isShowDownTip = true;
|
|||
|
} else if (this.isDownError && this.showDownError) {
|
|||
|
this.downText = this.downErrorText;
|
|||
|
this.isShowDownTip = true;
|
|||
|
}
|
|||
|
if (this.isShowDownTip) {
|
|||
|
setTimeout(() => {
|
|||
|
this.downHight = 0;
|
|||
|
this.isDownReset = true; // 重置高度
|
|||
|
this.scrollHeight = 0; // 重置滚动区域,使数据不满屏时仍可检查触发翻页
|
|||
|
setTimeout(() => {
|
|||
|
this.scrollAble = true; // 开启下拉
|
|||
|
this.isDownLoading = false; // 不显示加载中
|
|||
|
this.isShowDownTip = false;
|
|||
|
}, 300);
|
|||
|
}, 1000);
|
|||
|
} else {
|
|||
|
this.downHight = 0;
|
|||
|
this.isDownReset = true; // 重置高度
|
|||
|
this.scrollHeight = 0; // 重置滚动区域,使数据不满屏时仍可检查触发翻页
|
|||
|
this.scrollAble = true; // 开启下拉
|
|||
|
this.isDownLoading = false; // 不显示加载中
|
|||
|
this.isShowDownTip = false;
|
|||
|
}
|
|||
|
}
|
|||
|
},
|
|||
|
/* 显示上拉加载中 */
|
|||
|
showUpLoading() {
|
|||
|
this.isEmpty = false;
|
|||
|
this.isUpError = false;
|
|||
|
this.isUpFinish = false;
|
|||
|
this.isUpLoading = true;
|
|||
|
},
|
|||
|
/* 结束上拉加载 */
|
|||
|
hideUpLoading() {
|
|||
|
if (this.isUpLoading) {
|
|||
|
this.$nextTick(() => {
|
|||
|
this.isUpLoading = false;
|
|||
|
});
|
|||
|
}
|
|||
|
},
|
|||
|
/* 触发下拉刷新 */
|
|||
|
triggerPullDown() {
|
|||
|
if (this.pullDown && this.enablePullDown && !this.isDownLoading && !this.isUpLoading) {
|
|||
|
// 下拉加载中...
|
|||
|
this.showDownLoading(); // 下拉刷新中...
|
|||
|
this.page = 1; // 预先加一页
|
|||
|
this.pullType = 'down';
|
|||
|
this.pullDown && this.pullDown.call(this.$parent, this);
|
|||
|
}
|
|||
|
},
|
|||
|
|
|||
|
refresh() {
|
|||
|
this.scrollTo(0);
|
|||
|
this.page = 1;
|
|||
|
this.isEmpty = false;
|
|||
|
this.isDownSuccess = false;
|
|||
|
this.isDownError = false;
|
|||
|
this.isShowDownTip = false;
|
|||
|
this.isUpError = false;
|
|||
|
this.isUpFinish = false;
|
|||
|
this.isDownLoading = false;
|
|||
|
this.isUpLoading = false;
|
|||
|
if (this.pullDown && this.enablePullDown) {
|
|||
|
this.triggerPullDown();
|
|||
|
}
|
|||
|
},
|
|||
|
/* 正常加载成功 */
|
|||
|
success() {
|
|||
|
this.page++;
|
|||
|
if (this.isDownLoading) {
|
|||
|
this.isDownSuccess = true;
|
|||
|
}
|
|||
|
this.hideDownLoading();
|
|||
|
this.hideUpLoading();
|
|||
|
},
|
|||
|
/* 加载失败 */
|
|||
|
error() {
|
|||
|
if (this.isDownLoading) {
|
|||
|
this.isDownError = true;
|
|||
|
} else if (this.isUpLoading) {
|
|||
|
this.isUpError = true;
|
|||
|
}
|
|||
|
this.hideDownLoading();
|
|||
|
this.hideUpLoading();
|
|||
|
},
|
|||
|
/* 没有数据 */
|
|||
|
empty() {
|
|||
|
if (this.isDownLoading) {
|
|||
|
this.isDownSuccess = true;
|
|||
|
}
|
|||
|
this.isEmpty = true;
|
|||
|
this.isUpFinish = true;
|
|||
|
this.hideDownLoading();
|
|||
|
this.hideUpLoading();
|
|||
|
},
|
|||
|
/* 全部数据加载完毕 */
|
|||
|
finish() {
|
|||
|
this.hideDownLoading();
|
|||
|
this.hideUpLoading();
|
|||
|
this.isUpFinish = true;
|
|||
|
}
|
|||
|
}
|
|||
|
};
|
|||
|
</script>
|
|||
|
|
|||
|
<style lang="scss" scoped>
|
|||
|
.ccPullScroll {
|
|||
|
height: 100%;
|
|||
|
position: relative;
|
|||
|
|
|||
|
.ccPullScrollview {
|
|||
|
position: relative;
|
|||
|
width: 100%;
|
|||
|
height: 100%;
|
|||
|
overflow-y: auto;
|
|||
|
box-sizing: border-box;
|
|||
|
}
|
|||
|
|
|||
|
/* 定位的方式固定高度 */
|
|||
|
.is-fixed {
|
|||
|
z-index: 1;
|
|||
|
position: fixed;
|
|||
|
top: 0;
|
|||
|
left: 0;
|
|||
|
right: 0;
|
|||
|
bottom: 0;
|
|||
|
width: auto;
|
|||
|
height: auto;
|
|||
|
}
|
|||
|
|
|||
|
.cc-pull-down-wrap,
|
|||
|
.cc-pull-up-wrap {
|
|||
|
display: flex;
|
|||
|
justify-content: center;
|
|||
|
align-items: center;
|
|||
|
font-size: 28rpx;
|
|||
|
color: gray;
|
|||
|
}
|
|||
|
|
|||
|
.cc-pull-down-wrap {
|
|||
|
position: absolute;
|
|||
|
left: 0;
|
|||
|
width: 100%;
|
|||
|
transform: translateY(-100%);
|
|||
|
}
|
|||
|
|
|||
|
.cc-pull-up-wrap {
|
|||
|
min-height: 100rpx;
|
|||
|
}
|
|||
|
|
|||
|
/* 旋转loading */
|
|||
|
.cc-pull-loading-icon {
|
|||
|
width: 28rpx;
|
|||
|
height: 28rpx;
|
|||
|
border-radius: 50%;
|
|||
|
margin-right: 16rpx;
|
|||
|
border: 2rpx solid currentColor;
|
|||
|
border-bottom-color: transparent !important;
|
|||
|
}
|
|||
|
|
|||
|
/* 旋转动画 */
|
|||
|
.cc-pull-loading-rotate {
|
|||
|
animation: cc-pull-loading-rotate 0.6s linear infinite;
|
|||
|
}
|
|||
|
|
|||
|
@keyframes cc-pull-loading-rotate {
|
|||
|
0% {
|
|||
|
transform: rotate(0deg);
|
|||
|
}
|
|||
|
|
|||
|
100% {
|
|||
|
transform: rotate(360deg);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* 回到顶部的按钮 */
|
|||
|
.cc-pull-back-top {
|
|||
|
opacity: 0;
|
|||
|
pointer-events: none;
|
|||
|
transition: opacity 0.3s linear;
|
|||
|
|
|||
|
&.is-show {
|
|||
|
opacity: 1;
|
|||
|
pointer-events: auto;
|
|||
|
}
|
|||
|
|
|||
|
.default-back-top {
|
|||
|
position: absolute;
|
|||
|
right: 20rpx;
|
|||
|
width: 72rpx;
|
|||
|
height: 72rpx;
|
|||
|
border-radius: 50%;
|
|||
|
bottom: 30rpx;
|
|||
|
z-index: 99;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
</style>
|