移動前端--下拉刷新

  今天想說的是如何本身操刀作一個js的下拉刷新(js + h5 + css3)。既然是下拉,那麼應用場景固然就是在手持設備上。在JavaScript的世界裏,老是跟不少實用又華麗麗的效果接軌,這是一門頗有色彩的編程語言。目前網絡上也有不少很是優秀的js滑動插件,好比iscroll(最開始咱們就是用這款插件,真心很好用,並且解決了不少html的問題)。固然,我要講的徹底沒辦法和iscroll媲美,僅僅是簡簡單單的一角而已,主要目的在於對這點小知識的總結和分享。javascript

  以前也有講過,移動設備上對CSS3和Html5的支持至關的不錯,並且使用Css3咱們能夠輕鬆的實現滑動效果,不只不用擔憂性能問題,並且效果上也是無可挑剔的。那麼究竟須要作成什麼樣的效果呢?css

效果?嘿嘿,固然是相似淘寶那樣的。html

 

(這裏是用chrome模擬iphone5上的效果,關於如何模擬 這裏 有講過的,再也不贅述。)前端

 當往下拖動頁面的時候出現紅色箭頭所指向的灰色區域,隨着往下拖動的節奏,橘黃色的圈不斷的被填滿,而後停頓幾秒頁面刷新(不太明白的話就本身試試吧)。 額額,但是樓上的圖片貌似不是很適合作分析。下面以一個極爲粗糙的頁面作整個思路分析。java

 

佈局很重要!jquery

整個過程當中,佈局是決定編碼難度及js代碼量的重要因素,科學的佈局能夠帶來飛通常的感受(固然,不一樣的效果佈局上有所不一樣也是正常的)css3

查看粗製濫造的在線Demo 請戳這裏web

         

 

   圖1 所展現的是整個須要滑動的頁面的佈局結構。整個佈局使用的是一個div(藍色框部分)裏包含 加載數據的提示部分(簡稱「提示部分」)(紅色框部分) 和須要刷新的內容部分(簡稱「內容部分」)(文字部分)兩個div的結構。若是外層div向下移動,那麼裏面的提示部分和內容部分天然就跟着向下移動了(這樣是否是比同時使用js去控制兩個元素上下移動簡單多了?)ajax

   圖2 所展現的是正常的內容頁面(滑動完成以後,也是滑動以前的效果),佈局上主要是利用css3的transform屬性控制提示部分的隱藏和顯示。當translateY爲負時,整個div向上移動(圖2的效果),爲0時,整個提示部分就徹底展現出來(圖1的效果)chrome

 

   對於上面的描述若是沒看懂也別再看了(正在努力突破自我表達極限)。直接上代碼:

 

<html>
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>test</title> 
</head>
<body style="background-color: beige;">
    <div id="container" style="width:100%;border:solid 1px blue; transform:translate(0px,-61px)">
        <div style="height:50px; line-height:50px; text-align:center; width:100%; border:solid 1px red;">
            努力加載中...
        </div> 
        <div style="width:100%; line-height:30px;background-color:#F2F2F2; font-size:17px; font-family:'Adobe Garamond Pro'">
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JavaScript一種直譯式腳本語言,是一種動態類型、弱類型、基於原型的語言,內置支持類型。它的解釋器被稱爲JavaScript引擎,爲瀏覽器的一部分,普遍用於客戶端的腳本語言,最先是在HTML(標準通用標記語言下的一個應用)網頁上使用,用來給HTML網頁增長動態功能。
            在1995年時,由Netscape公司的Brendan Eich,在網景導航者瀏覽器上首次設計實現而成。由於Netscape與Sun合做,Netscape管理層但願它外觀看起來像Java,所以取名爲JavaScript。但實際上它的語法風格與Self及Scheme較爲接近。[1]
            爲了取得技術優點,微軟推出了JScript,CEnvi推出ScriptEase,與JavaScript一樣可在瀏覽器上運行。爲了統一規格,由於JavaScript兼容於ECMA標準,所以也稱爲ECMAScript。

            JavaScript是一種屬於網絡的腳本語言,已經被普遍用於Web應用開發,經常使用來爲網頁添加各式各樣的動態功能,爲用戶提供更流暢美觀的瀏覽效果。一般JavaScript腳本是經過嵌入在HTML中來實現自身的功能的。[3]
            是一種解釋性腳本語言(代碼不進行預編譯)。[4]
            主要用來向HTML(標準通用標記語言下的一個應用)頁面添加交互行爲。[4]
            能夠直接嵌入HTML頁面,但寫成單獨的js文件有利於結構和行爲的分離。[4]
            跨平臺特性,在絕大多數瀏覽器的支持下,能夠在多種平臺下運行(如Windows、Linux、Mac、Android、iOS等)。
            Javascript腳本語言同其餘語言同樣,有它自身的基本數據類型,表達式和算術運算符及程序的基本程序框架。Javascript提供了四種基本的數據類型和兩種特殊數據類型用來處理數據和文字。而變量提供存放信息的地方,表達式則能夠完成較複雜的信息處理。[5]
        </div>

    </div>

</body>
</html> 
<!--JQuery是那麼的好用,這種狀況下怎麼能沒有它呢!-->
<script type="text/javascript" src="http://libs.baidu.com/jquery/1.11.1/jquery.min.js"></script>
 
Html相關代碼

 

    佈局上大體就是這樣的結構,那麼~

    JavaScript該作什麼呢?  

    一、根據滑動軌跡動態調整滑塊位置(transfrom=>translate);

    二、根據滑動的距離判斷是否執行刷新(或數據加載)。

    固然,若是滑動結束後使用ajax從新加載頁面數據,還將涉及到一個頁面向上滑動並隱藏提示部分的效果。

    

    大體思路:

    (前提條件:當前元素已滑動至頂部) 

    一、當鼠標左鍵按下(移動設備上的touchstart事件)的時候記錄下當前鼠標位置的 Y軸座標;

    二、當鼠標移動的時候(touchmove事件),記錄下鼠標的Y 軸座標判斷滑動軌跡並進行相應的滑塊移動;

    三、當鼠標左鍵鬆開(touchend事件)的時候,經過對比鼠標開始和結束的Y軸座標的距離判斷是否應該刷新頁面(或從新加載數據)。

 亮代碼,秀畫風:

/*
    *obj--滑動對象
    *offset--滑動距離(當滑動距離大於等於offset時將調用callback)
    *callback--滑動完成後的回調函數
    */
    var slide = function (obj, offset, callback) {
        var start,
            end,
            isLock = false,//是否鎖定整個操做
            isCanDo = false,//是否移動滑塊
            isTouchPad = (/hp-tablet/gi).test(navigator.appVersion),
            hasTouch = 'ontouchstart' in window && !isTouchPad;

        //將對象轉換爲jquery的對象
        obj = $(obj);

        var objparent = obj.parent();

        /*操做方法*/
        var fn =
            {
                //移動容器
                translate: function (diff) {
                    obj.css({
                        "-webkit-transform": "translate(0," + diff + "px)",
                        "transform": "translate(0," + diff + "px)"
                    });
                },
                //設置效果時間
                setTranslition: function (time) {
                    obj.css({
                        "-webkit-transition": "all " + time + "s",
                        "transition": "all " + time + "s"
                    });
                },
                //返回到初始位置
                back: function () {
                    fn.translate(0 - offset);
                    //標識操做完成
                    isLock = false;
                }
            };

        //滑動開始
        obj.bind("touchstart", function (e) {

            if (objparent.scrollTop() <= 0 && !isLock) {
                var even = typeof event == "undefined" ? e : event;
                //標識操做進行中
                isLock = true;
                isCanDo = true;
                //保存當前鼠標Y座標
                start = hasTouch ? even.touches[0].pageY : even.pageY;
                //消除滑塊動畫時間
                fn.setTranslition(0);
            }
        });

        //滑動中
        obj.bind("touchmove", function (e) {

            if (objparent.scrollTop() <= 0 && isCanDo) {

                var even = typeof event == "undefined" ? e : event;

                //保存當前鼠標Y座標
                end = hasTouch ? even.touches[0].pageY : even.pageY;

                if (start < end) {
                    even.preventDefault();
                    //消除滑塊動畫時間
                    fn.setTranslition(0);
                    //移動滑塊
                    fn.translate(end - start - offset);
                }

            }
        });


        //滑動結束
        obj.bind("touchend", function (e) {
            if (isCanDo) {
                isCanDo = false;
                //判斷滑動距離是否大於等於指定值
                if (end - start >= offset) {
                    //設置滑塊回彈時間
                    fn.setTranslition(1);
                    //保留提示部分
                    fn.translate(0);

                    //執行回調函數
                    if (typeof callback == "function") {
                        callback.call(fn, e);
                    }
                } else {
                    //返回初始狀態
                    fn.back();
                }
            }
        });
    }
JavaScript相關代碼

 

 代碼分析:

 一、參數:obj,要滑動的對象;offset,提示部分的transform的值(代碼中是 transform:translate(0px,-61px) ,那麼這裏就是61);callback,回調函數,在下拉完成後調用的函數(頁面刷新或數據加載)。

 

 二、爲何是transform不是margin? 

  由於transform不會引發重繪,相比margin更流暢,性能更好。可是transfrom有個比較好玩的地方,若是translateY的值爲負數(當前元素上移xx像素)下方元素不會跟着上移(margin會上移),在這點上它和margin是有區別的。 注意,這裏的-webkit-transform的存在是有必要的,由於有些瀏覽器識別不了transform,好比微信內置瀏覽(個人手機上是這樣的)。爲了兼容性,多扣幾個字母是值得的。

 

 三、關於transition設置爲0s。

  爲何要在touchstart的時候把transition的值設置爲0秒呢?transition的做用是爲元素屬性的變化添加過渡效果,例如一個框變大,咱們設置爲transition爲1s,那麼這個框就是在1s內變大到指定大小。第一個參數表示設置過渡效果的 CSS 屬性的名稱(如:margin,transform;all表示全部),第二個參數表示過渡的時間。 代碼中設置transition的目的是在於滑動結束後(手指離開屏幕)爲滑塊回彈添加過渡效果,這樣看上去就不會那麼突兀。固然,這個過渡效果一樣會應用到數據加載完成後提示部分的隱藏上。設置爲0是爲了取消在滑動過程當中的滑塊過渡效果,咱們手指往下滑動的時候,滑塊會跟這向下移動,這樣就有了滑動滑塊的效果。若是這個時候不取消transition就會出現滑塊抖動的效果(嘿嘿,有興趣的話能夠試試這種感受。)。整個過程當中transition是至關重要的。

 

 四、關於isLock和isCanDo.

   這兩個變量的做用在於防止二次滑動,在第一次滑動後數據加載完成以前不容許有第二次的滑動。當滑動開始的時候講isLock和isCanDo都設置爲True,表示容許後面兩個事件裏的代碼能夠正常運行,當滑動結束後isCanDo設置爲false表示在isLock被設置爲True以前(整個操做完成以前)全部的事件代碼均不可用(不執行下拉數據加載等相關動做)

 

五、如何使用?

這個比較就簡單,但也比較重要。

    $(function () {

        slide("#container", 61, function (e) {
            var that = this;

            setTimeout(function () {
                that.back.call();
            }, 2000);

        });
    });
View Code

  代碼中的setTimeout是用於模擬ajax加載數據的效果,加載數據這部分就沒有再單獨寫過了。JavaScript的回調函數是用着最順手的特性之一。這裏在數據加載完成後須要調用一個back方法,這個方法目的就是重置slide裏的各類狀態。關於這種傳來傳去的方式給人的感受有點像作地下工做,不太容易被發現,可暫時也沒有想到更好的解決方案。

 

  最後:

若是各位有什麼好的方法或想法,歡迎你們在樓下@我

相關文章
相關標籤/搜索