用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'">
                    <i class="icon mtui-icon-select-o"></i>
                    <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,可以参考这里

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

源代码

猜你喜欢

转载自www.cnblogs.com/blogs-xlf/p/10139788.html
今日推荐