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

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

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

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 -.-

源代码

posted @ 2018-12-18 20:25  =.=  阅读(1647)  评论(0编辑  收藏  举报