ZDropDownRefresh.vue
<template lang="html">
<div
class="refresh-moudle"
@touchstart="touchStart($event)"
@touchmove="touchMove($event)"
@touchend="touchEnd($event)"
ref="myrefresh"
:style="{ transform: 'translate3d(0,' + top + 'px, 0)' }"
>
<header class="pull-refresh">
<slot name="pull-refresh">
<div class="down-tip" v-if="dropDownState === 1 || dropDownState === 2">
<i
ref="icon"
class="css-icon icon-upward"
:class="{ active: dropDownState === 2 }"
></i>
<span class="down-text" v-if="dropDownState === 1">{{
dropDownInfo.downText
}}</span>
<span class="down-text" v-if="dropDownState === 2">{{
dropDownInfo.upText
}}</span>
</div>
<div class="refresh-tip" v-if="dropDownState === 3">
<span class="refresh-text">{{ dropDownInfo.refreshText }}</span>
</div>
</slot>
</header>
<slot></slot>
</div>
</template>
<script>
export default {
props: {
onRefresh: {
type: Function,
required: false,
},
},
data() {
return {
defaultOffset: 40, // 默认高度, 相应的修改.releshMoudle的margin-top和.down-tip, .up-tip, .refresh-tip的height
top: 0,
scrollIsToTop: 0,
startY: 0,
isDropDown: false, // 是否下拉
isRefreshing: false, // 是否正在刷新
isDropInTop: false, //开始下拉时是否在滚动条已在最上面
dropDownState: 1, // 显示1:下拉可以刷新, 2:松开立即刷新, 3:正在刷新数据中...
dropDownInfo: {
downText: "下拉可以刷新",
upText: "松开立即刷新",
refreshText: "正在刷新数据...",
refreshImg: "loading.png",
},
};
},
created() {
if (document.querySelector(".down-tip")) {
// 获取不同手机的物理像素(dpr),以便适配rem
this.defaultOffset =
document.querySelector(".down-tip").clientHeight || this.defaultOffset;
}
},
methods: {
/**
* 触摸开始,手指点击屏幕时
* @param {object} e Touch 对象包含的属性
*/
touchStart(e) {
let dom = this.$refs["myrefresh"];
this.scrollIsToTop =
dom.scrollTop || window.pageYOffset || document.body.scrollTop; // safari 获取scrollTop用window.pageYOffset
if (this.scrollIsToTop === 0) {
this.startY = e.targetTouches[0].pageY;
this.isDropInTop = true;
} else {
this.isDropInTop = false;
}
},
/**
* 接触点改变,滑动时
* @param {object} e Touch 对象包含的属性
*/
touchMove(e) {
if (this.isDropInTop) {
if (e.targetTouches[0].pageY > this.startY) {
// 下拉
this.isDropDown = true;
if (!this.isRefreshing) {
// 拉动的距离
let diff =
e.targetTouches[0].pageY - this.startY - this.scrollIsToTop;
this.top =
Math.pow(diff, 0.8) +
(this.dropDownState === 3 ? this.defaultOffset : 0);
if (this.top >= this.defaultOffset) {
this.dropDownState = 2;
e.preventDefault();
} else {
this.dropDownState = 1;
// 去掉会导致ios无法刷新
e.preventDefault();
}
}
} else {
this.isDropDown = false;
this.dropDownState = 1;
}
}
},
/**
* 触摸结束,手指离开屏幕时
*/
touchEnd() {
if (this.isDropInTop) {
if (this.isDropDown && !this.isRefreshing) {
if (this.top >= this.defaultOffset) {
this.refresh();
this.isRefreshing = true;
} else {
// cancel refresh
this.isRefreshing = false;
this.isDropDown = false;
this.dropDownState = 1;
this.top = 0;
}
}
}
},
/**
* 刷新
*/
refresh() {
this.dropDownState = 3;
this.top = this.defaultOffset;
setTimeout(() => {
this.onRefresh(this.refreshDone);
}, 300);
},
/**
* 刷新完成
*/
refreshDone() {
this.isRefreshing = false;
this.isDropDown = false;
this.dropDownState = 1;
this.top = 0;
},
},
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.refresh-moudle {
width: 100%;
height: calc(100% + 40px);
margin-top: -40px;
-webkit-overflow-scrolling: touch; /* ios5+ */
overflow-y: scroll;
}
.pull-refresh {
width: 100%;
color: #999;
height: 40px;
transition-duration: 200ms;
font-size: 18px;
}
.refresh-moudle .down-tip,
.up-tip,
.refresh-tip {
display: flex;
align-items: center;
justify-content: center;
height: 40px;
}
.css-icon {
display: inline-block;
height: 1em;
width: 1em;
font-size: 20px;
margin-right: 10px;
box-sizing: border-box;
text-indent: -9999px;
vertical-align: middle;
position: relative;
}
.css-icon::before,
.css-icon::after {
content: "";
box-sizing: inherit;
position: absolute;
left: 50%;
top: 50%;
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
.icon-upward::before {
height: 0.65em;
width: 0.65em;
border-style: solid;
border-width: 2px 0 0 2px;
-ms-transform: translate(-50%, -50%) rotate(45deg);
transform: translate(-50%, -50%) rotate(45deg);
}
.icon-upward::after {
height: 0.8em;
border-left: 2px solid;
top: 55%;
}
.icon-upward.active {
transform: rotate(180deg);
transition: transform 0.3s;
}
@keyframes anticlockwise {
0% {
transform: rotate(-180deg);
}
100% {
transform: rotate(0deg);
}
}
@keyframes clockwise {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(-180deg);
}
}
.refresh-img {
width: 35px;
height: 35px;
margin-right: 15px;
animation: rotating 1.5s linear infinite;
}
@keyframes rotating {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(1turn);
}
}
</style>
<template>
<div class="home">
<div class="msg_list">
<z-refresh :onRefresh="onRefresh">
<div class="msg_item" @click="notice_click">
<div class="msg_top">11111</div>
<div class="msg_info">
123456
</div>
<div class="msg_time">2021-08-28</div>
</div>
<div class="loadmore" @click="onLoadMore">点击加载更多</div>
</z-refresh>
</div>
</div>
</template>
<script>
import ZDropDownRefresh from "../components/ZDropDownRefresh";
export default {
components: {
"z-refresh": ZDropDownRefresh,
},
methods: {
/**
* 下拉刷新
*/
onRefresh(done) {
// 如果下拉刷新和上拉加载同时使用,下拉时初始化上拉的数据
console.info("下拉刷新");
done();
},
onLoadMore() {
console.info("加载更多");
},
},
};
</script>