1. 程式人生 > >用vue寫一個仿app下拉重新整理的元件

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

原始碼