用vue写一个仿app下拉刷新的组件

时间:2023-11-23 11:32:44

如果你用vue弄移动端的页面,那么下拉刷新还是比较常见的场景,下面来研究如何写一个下拉刷新的组件(先上图);

用vue写一个仿app下拉刷新的组件

用vue写一个仿app下拉刷新的组件

用vue写一个仿app下拉刷新的组件

用vue写一个仿app下拉刷新的组件

由于节省大家的时间,样式就不贴出来了。

html结构也不必介绍了,直接看代码吧-.-

        <transition>
<div class="refresh-wrapper" ref="refresh">
<div class="refresh-inner">
<div class="refresh-pull" v-show="state==='pull'">
<span>下拉刷新-.-</span>
</div>
<div class="refresh-loading" v-show="state==='loading'">
<span>刷新中...~.~</span>
</div> <div class="refresh-end" v-show="state==='end'">
<span>刷新完成!^.^</span>
</div>
</div> </div>
</transition>

核心思路及步骤

  1. document绑定touch事件
document.addEventListener('touchstart',this.touchStart,false);
document.addEventListener('touchmove',this.touchMove,false);
document.body.addEventListener('touchend',this.touchEnd,false);
  1. touchStart细节

    2.1 判断状态,这是为了防止在刷新时多次触发。可以定义一个变量保存状态,状态值为pull, loading, end;

    2.2 记录开始的位置,Y轴的就可以了;

    2.3 获取当前touch的对象? 虽然我们已经把事件绑定在document上了,但是在有局部滚动的时候,那么向下滑动的时候就会有冲突,这个时候可以获取到当前touch的对象,后面做处理;
touchStart(e){
if(this.state === 'loading') return; this.startY = e.touches[0].clientY;
this.getTouchTarget(e.target); } getTouchTarget(elm){
let currentNode = elm;
while(currentNode && currentNode.tagName !== "HTML" &&
currentNode.tagName !== "BODY" && currentNode.nodeType === 1){
let overflowY = window.getComputedStyle(currentNode).overflowY;
if(overflowY === "scroll" || overflowY === "auto"){
this.currentNode = currentNode;
this.firstNode = currentNode.firstElementChild; //记录局部滚动的第一个子元素
break;
}
currentNode = currentNode.parentNode;
} }
  1. touchMove细节

    3.1 判断当前滑动的区域是否局部滚动,如果是,通过判断父元素和子元素的getBoundingClientRect().top的差值是否小于0来判断是否滚动到顶部

    3.2 判断一些其他的条件

    3.3 记录滑动的距离

    3.4 改变视图
touchMove(e){
let firstTop=0, currentTop=0;
if(this.firstNode){
firstTop = this.firstNode.getBoundingClientRect().top;
currentTop = this.currentNode.getBoundingClientRect().top;
}
let range = (e.touches[0].clientY - this.startY);
if( document.documentElement.scrollTop>0 || this.state === 'loading' || firstTop-currentTop <0 || range<0) return; range = range*0.75 > this.maxRange? this.maxRange : range; this.translate = range;
this.changeView(); } changeView(){
//这里针对transfrom对fixed定位的bug做的降级处理
if(this.isFixed){
this.$refs.refresh.style.transform=`translate3d(0,${this.translate}px,0)`;
}else{
document.body.style.transform = `translate3d(0,${this.translate}px,0)`;
}
}
  1. touchEnd细节

    4.1 判断状态

    4.2 判断滑动距离是否到可刷新距离,如果是,调用刷新api

    4.3 改变视图
touchEnd(e){
if(this.state === 'loading') return; if(this.translate && this.translate >= this.maxRange){
this.translate = this.maxRange/2; this.refresh();
}else{
this.translate = 0;
}
this.rotate = 0;
this.changeView();
} refresh(){
this.state = 'loading';
console.log('更新中...');
this.$emit('refresh'); //父组件监听refresh方法,并在异步回调中调用子组件的refreshEnd方法,可通过this.$refs.名称.refreshEnd()方法调用 } refreshEnd(){
let _this = this;
this.state = 'end'; setTimeout(()=>{ _this.translate = 0;
_this.changeView();
},1000); setTimeout(()=>{
_this.state = 'pull';
},1300) console.log('更新完成...');
}

注意

1.如果有fixed布局,要传isFixed='true',否则会有bug。关于transform 与fixed的bug,可以参考这里

2. 不建议用在过于复杂的布局,可能有未知bug -.-

源代码