DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> JavaScript入門知識 >> JavaScript技巧 >> JS+CSS實現下拉刷新/上拉加載插件
JS+CSS實現下拉刷新/上拉加載插件
編輯:JavaScript技巧     

閒來無事,寫了一個當下比較常見的下拉刷新/上拉加載的jquery插件,代碼記錄在這裡,有興趣將代碼寫成插件與npm包可以留言。

體驗地址:http://owenliang.github.io/pullToRefresh/

項目地址:https://github.com/owenliang/pullToRefresh

實現注意:

利用transition做動畫時,優先使用transform:translate取代top,後者動畫流暢度存在問題。

各移動浏覽器對手勢觸摸的處理不同(簡單羅列如下),但是下面的應對方案又會導致部分浏覽器的overflow:scroll失效,總之難以兼容:

微信浏覽器下拉自帶回彈動畫:可以禁止document的touchmove事件默認處理行為。

谷歌浏覽器下拉自帶刷新功能:利用屬性touch-action: none可以禁掉。

針對上述問題,我的建議是滾動一律用iscroll5插件模擬實現(非overflow:scroll),然後利用上面的方法禁掉浏覽器的默認touchmove行為。

transition如果有多個屬性,那麼transitionend回調會為每個屬性回調一次,因此遇到其中任意一個回調就應該把css和transitionend回調都刪除掉。

浏覽器在執行JS代碼時沒有機會重繪UI,因此在使用transition的時候一定要注意把修改動畫終止CSS的代碼通過setTimeout延遲一會執行。

貼代碼上首頁,歡迎留言交流,需一位有興趣有時間的朋友合作,主要做2件事:

1)插件改為NPM包。

2)基於pullToRefresh庫,開發類似"今日頭條"的左右滑動UI。

pullToRefresh.js:

/**
 * 為指定的容器添加滾動條,支持下拉刷新與上拉加載功能
 * @param container 需要滾動的容器,要求設置css: position!=static,height=
 * @param option 配置項,詳見下方defaultOption說明
 * @return 返回對象用於操控此區域,當前暴露了iscroll的refresh函數,當你在插件之外向滾動區域增加/刪除內容後應該主動調用一次
 * @description
 *
 * 2017-03-29
 * 1)支持上拉加載
 * 2017-03-30
 * 1)改為jquery靜態函數插件
 * 2)支持關閉下拉刷新或上拉加載
 */
$.installPullToRefresh =
function (container, option) {
  // 起始觸摸位置
  var touchStartY = 0;
  // 起始圖標位置
  var pullStartY = 0;
  // 當前的觸摸事件
  var touchEvent = null;
  // 當前的刷新事件
  var refreshEvent = null;
  // 當前圖標位置
  var curY = -55;
  // 當前的加載事件
  var loadEvent = null;
  // 默認參數
  var defaultOption = {
    // 刷新相關
    noRefresh: false, // 關閉下拉刷新特性
    pauseBound: 40, // 觸發刷新的位置(也是圖標loading暫停的位置)
    lowerBound: 80, // 最大下拉到多少px
    loadImg: "load.png", // loading圖片
    pullImg: "pull.png", // 下拉圖片
    onRefresh: function (refreshDone) { // 刷新數據回調
      setTimeout(function() { // 默認不做任何事
        refreshDone();
      }, 0);
    },
    // 加載相關
    noLoad: false, // 關閉上拉加載特性
    bottomHeight: 1, // 距離滾動條底部多少px發起刷新
    onLoad: function (loadDone) {
      setTimeout(function() {
        loadDone();
      }, 0);
    },
  };
  var finalOption = $.extend(true, defaultOption, option);
  // 創建iscroll5滾動區域
  var iscroll = new IScroll(container, {
    bounce: false,
  });
  // 關閉上拉加載特性
  if (!finalOption.noLoad) {
    // 監聽滾動結束事件,用於上拉加載
    iscroll.on('scrollEnd', function () {
      // 有滾動條的情況下,才允許上拉加載
      if (iscroll.maxScrollY < 0) { // maxScrollY<0表明出現了滾動條
        var bottomDistance = (iscroll.maxScrollY - iscroll.y) * -1;
        // 距離底部足夠近,觸發加載
        if (bottomDistance <= finalOption.bottomHeight) {
          // 當前沒有刷新和加載事件正在執行
          if (!loadEvent && !refreshEvent) {
            loadEvent = {}; // 生成新的加載事件
            finalOption.onLoad(function (error, msg) {
              loadEvent = null; // 清理當前的加載事件
              // 延遲重繪滾動條
              setTimeout(function () {
                iscroll.refresh();
              }, 0);
            });
          }
        }
      }
    });
  }
  // 關閉下拉刷新特性
  if (!finalOption.noRefresh) {
    // 緊鄰滾動區域,容納刷新圖標
    var pullContainer = $('<div class="pullContainer"></div>')
    // 創建小圖標
    var pullToRefresh = $('<div class="pullToRefresh"><img src="' + finalOption.pullImg + '"></div>');
    // 保留小圖標的快捷方式
    var pullImg = pullToRefresh.find("img");
    // 小圖標加入到容器
    pullContainer.append(pullToRefresh);
    // 小圖標容器添加到滾動區域之前
    $(container).before(pullContainer);
    // 預加載loadImg
    $('<img src="' + finalOption.loadImg + '">');
    // 設置transform的函數
    function cssTransform(node, content) {
      node.css({
        '-webkit-transform' : content,
        '-moz-transform'  : content,
        '-ms-transform'   : content,
        '-o-transform'   : content,
        'transform'     : content,
      });
    }
    // 調整小圖標位置,角度,透明度
    function goTowards(translateY, rotate, opcaticy) {
      // 更新當前小圖標的位置,獲取css(transform)比較麻煩,所以每次變更時自己保存
      curY = translateY;
      // 旋轉圖標(根據抵達lowerBound的比例旋轉,最大轉1圈)
      if (rotate === undefined) {
        rotate = (curY / finalOption.lowerBound) * 360;
      }
      // 透明度根據抵達pauseBound的比例計算
      if (opcaticy === undefined) {
        opcaticy = (curY / finalOption.pauseBound) * 1;
        if (opcaticy > 1) {
          opcaticy = 1;
        }
      }
      // 改變位置和旋轉角度
      cssTransform(pullToRefresh, "translateY(" + translateY + "px) translateZ(0)" + "rotateZ(" + rotate + "deg)");
      // 改變透明度
      pullToRefresh.css("opacity", opcaticy);
    }
    // 開啟回彈動畫
    function tryStartBackTranTop() {
      // 啟動回彈動畫
      pullToRefresh.addClass("backTranTop");
      // 判斷是否觸發刷新
      if (curY >= finalOption.pauseBound) {
        goTowards(finalOption.pauseBound);
        // 回彈動畫結束發起刷新
        pullToRefresh.on('transitionend webkitTransitionEnd oTransitionEnd', function (event) {
          // 由於transitionend會對每個屬性回調一次,所以只處理其中一個
          if (event.originalEvent.propertyName == "transform") {
            // 暫停動畫
            pullToRefresh.removeClass("backTranTop");
            pullToRefresh.unbind();
            // 透明度重置為1
            goTowards(finalOption.pauseBound, undefined, 1);
            // 切換圖片為loading圖
            pullImg.attr("src", finalOption.loadImg);
            // 因為anamition會覆蓋transform的原因,使用top臨時定位元素
            pullToRefresh.addClass("loadingAnimation");
            pullToRefresh.css("top", finalOption.pauseBound + "px");
            // 回調刷新數據,最終應將refreshEvent傳回校驗
            finalOption.onRefresh(function (error, msg) {
              // 用戶回調時DOM通常已經更新, 需要通知iscroll調整(官方建議延遲執行,涉及到浏覽器重繪問題)
              setTimeout(function () {
                iscroll.refresh();
              }, 0);
              // 重置角度,切換為pull圖
              goTowards(finalOption.pauseBound);
              // 取消animation,重置top
              pullToRefresh.removeClass("loadingAnimation");
              pullToRefresh.css("top", "");
              // 延遲過渡動畫100毫秒,給浏覽器重繪的機會
              setTimeout(function () {
                // 切換為pull圖
                pullImg.attr("src", finalOption.pullImg);
                // 恢復動畫
                pullToRefresh.addClass("backTranTop");
                // 刷新完成
                refreshEvent = null;
                // 彈回頂部
                goTowards(-55);
              }, 100);
            });
          }
        });
      } else {
        goTowards(-55); // 彈回頂部
        refreshEvent = null; // 未達成刷新觸發條件
      }
    }
    // 父容器注冊下拉事件
    $(container).on("touchstart", function (event) {
      // 新的觸摸事件
      touchEvent = {};
      // 有一個刷新事件正在進行
      if (refreshEvent) {
        return;
      }
      // 只有滾動軸位置接近頂部, 才可以生成新的刷新事件
      if (iscroll.y < -1 * finalOption.lowerBound) {
        return;
      }
      // 一個新的刷新事件
      refreshEvent = touchEvent;
      touchStartY = event.originalEvent.changedTouches[0].clientY;
      pullStartY = curY;
      // 如果存在,則關閉回彈動畫與相關監聽
      pullToRefresh.removeClass("backTranTop");
      pullToRefresh.unbind();
      // 切換為pull圖
      pullImg.attr("src", finalOption.pullImg);
    }).on("touchmove", function (event) {
      // 在刷新未完成前觸摸,將被忽略
      if (touchEvent != refreshEvent) {
        return;
      }
      var touchCurY = event.originalEvent.changedTouches[0].clientY;
      var touchDistance = touchCurY - touchStartY; // 本次移動的距離
      var curPullY = pullStartY + touchDistance; // 計算圖標應該移動到的位置
      // 向下不能拉出范圍
      if (curPullY > finalOption.lowerBound) {
        curPullY = finalOption.lowerBound;
      }
      // 向上不能拉出范圍
      if (curPullY <= -55) {
        curPullY = -55;
      }
      // 更新圖標的位置
      goTowards(curPullY);
    }).on("touchend", function (event) {
      // 在刷新未完成前觸摸,將被忽略
      if (touchEvent != refreshEvent) {
        return;
      }
      // 嘗試啟動回彈動畫
      tryStartBackTranTop();
    });
  }
  // 初始化iscroll
  setTimeout(function() {
    iscroll.refresh();
  }, 0);
  // 返回操作此區域的工具對象
  return {
    // 用戶如果在下拉刷新之外修改了滾動區域的內容,需要主動調用refresh
    refresh: function() {
      // 延遲以便配合浏覽器重繪
      setTimeout(function() {
        iscroll.refresh();
      }, 0);
    },
    // 觸發下拉刷新
    triggerPull: function() {
      // 正在刷新或者禁止刷新
      if (refreshEvent || finalOption.noRefresh) {
        return false;
      }
      // 暫停可能正在進行的最終階段回彈動畫
      pullToRefresh.removeClass("backTranTop");
      // 小圖標移動到lowerbound位置
      goTowards(finalOption.lowerBound);
      // 創建新的刷新事件,占坑可以阻止在setTimeout之前的觸摸引起刷新
      refreshEvent = {};
      // 延遲到浏覽器重繪
      setTimeout(function() {
        tryStartBackTranTop();
      }, 100);
    },
  };
};
Contact GitHub API Training Shop Blog About
© 2017 GitHub, Inc. Terms Privacy Security Status Help

pullToRefresh.css:

.pullToRefresh {
  position:absolute;
  left:0;
  right:0;
  margin:auto;
  width: 50px;
  height: 50px;
  z-index: 10;
  opacity: 1;
  transform:translateY(-55px) translateZ(0) rotateZ(0deg);
  -ms-transform:translateY(-55px) translateZ(0) rotateZ(0deg);   /* IE 9 */
  -moz-transform:translateY(-55px) translateZ(0) rotateZ(0deg);   /* Firefox */
  -webkit-transform:translateY(-55px) translateZ(0) rotateZ(0deg); /* Safari 和 Chrome */
  -o-transform:translateY(-55px) translateZ(0) rotateZ(0deg);   /* Opera */
}
.backTranTop
{
  transition: transform 0.8s ease, opacity 0.8s ease;
  -moz-transition: transform 0.8s ease, opacity 0.8s ease; /* Firefox 4 */
  -webkit-transition: transform 0.8s ease, opacity 0.8s ease; /* Safari 和 Chrome */
  -o-transition: transform 0.8s ease, opacity 0.8s ease; /* Opera */
}
.pullContainer {
  position:relative;
}
.pullToRefresh img {
  display:block;
  width: 40px;
  height: 40px;
  /* 讓img居中在.pullToRefresh中 */
  position: absolute;
  top: 0;
  bottom: 0;
  left:0;
  right:0;
  margin:auto;
}
/* loading旋轉動畫 */
.loadingAnimation
{
  animation: loadingFrame 1s infinite;
  -moz-animation: loadingFrame 1s infinite;  /* Firefox */
  -webkit-animation: loadingFrame 1s infinite;  /* Safari 和 Chrome */
  -o-animation: loadingFrame 1s infinite;  /* Opera */
}
@keyframes loadingFrame
{
  from {
    transform: rotateZ(360deg);
  }
  to {
    transform: rotateZ(0deg);
  }
}
@-moz-keyframes loadingFrame /* Firefox */
{
  from {
    transform: rotateZ(360deg);
  }
  to {
    transform: rotateZ(0deg);
  }
}
@-webkit-keyframes loadingFrame /* Safari 和 Chrome */
{
  from {
    transform: rotateZ(360deg);
  }
  to {
    transform: rotateZ(0deg);
  }
}
@-o-keyframes loadingFrame /* Opera */
{
  from {
    transform: rotateZ(360deg);
  }
  to {
    transform: rotateZ(0deg);
  }
}

以上所述是小編給大家介紹的JS+CSS實現下拉刷新/上拉加載插件,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對網站的支持!

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved