JS+CSS實現(xiàn)下拉刷新/上拉加載插件
閑來無事,寫了一個當(dāng)下比較常見的下拉刷新/上拉加載的jquery插件,代碼記錄在這里,有興趣將代碼寫成插件與npm包可以留言。
體驗地址:http://owenliang.github.io/pullToRefresh/
項目地址:https://github.com/owenliang/pullToRefresh
實現(xiàn)注意:
利用transition做動畫時,優(yōu)先使用transform:translate取代top,后者動畫流暢度存在問題。
各移動瀏覽器對手勢觸摸的處理不同(簡單羅列如下),但是下面的應(yīng)對方案又會導(dǎo)致部分瀏覽器的overflow:scroll失效,總之難以兼容:
微信瀏覽器下拉自帶回彈動畫:可以禁止document的touchmove事件默認(rèn)處理行為。
谷歌瀏覽器下拉自帶刷新功能:利用屬性touch-action: none可以禁掉。
針對上述問題,我的建議是滾動一律用iscroll5插件模擬實現(xiàn)(非overflow:scroll),然后利用上面的方法禁掉瀏覽器的默認(rèn)touchmove行為。
transition如果有多個屬性,那么transitionend回調(diào)會為每個屬性回調(diào)一次,因此遇到其中任意一個回調(diào)就應(yīng)該把css和transitionend回調(diào)都刪除掉。
瀏覽器在執(zhí)行JS代碼時沒有機會重繪UI,因此在使用transition的時候一定要注意把修改動畫終止CSS的代碼通過setTimeout延遲一會執(zhí)行。
貼代碼上首頁,歡迎留言交流,需一位有興趣有時間的朋友合作,主要做2件事:
1)插件改為NPM包。
2)基于pullToRefresh庫,開發(fā)類似"今日頭條"的左右滑動UI。
pullToRefresh.js:
/**
* 為指定的容器添加滾動條,支持下拉刷新與上拉加載功能
* @param container 需要滾動的容器,要求設(shè)置css: position!=static,height=
* @param option 配置項,詳見下方defaultOption說明
* @return 返回對象用于操控此區(qū)域,當(dāng)前暴露了iscroll的refresh函數(shù),當(dāng)你在插件之外向滾動區(qū)域增加/刪除內(nèi)容后應(yīng)該主動調(diào)用一次
* @description
*
* 2017-03-29
* 1)支持上拉加載
* 2017-03-30
* 1)改為jquery靜態(tài)函數(shù)插件
* 2)支持關(guān)閉下拉刷新或上拉加載
*/
$.installPullToRefresh =
function (container, option) {
// 起始觸摸位置
var touchStartY = 0;
// 起始圖標(biāo)位置
var pullStartY = 0;
// 當(dāng)前的觸摸事件
var touchEvent = null;
// 當(dāng)前的刷新事件
var refreshEvent = null;
// 當(dāng)前圖標(biāo)位置
var curY = -55;
// 當(dāng)前的加載事件
var loadEvent = null;
// 默認(rèn)參數(shù)
var defaultOption = {
// 刷新相關(guān)
noRefresh: false, // 關(guān)閉下拉刷新特性
pauseBound: 40, // 觸發(fā)刷新的位置(也是圖標(biāo)loading暫停的位置)
lowerBound: 80, // 最大下拉到多少px
loadImg: "load.png", // loading圖片
pullImg: "pull.png", // 下拉圖片
onRefresh: function (refreshDone) { // 刷新數(shù)據(jù)回調(diào)
setTimeout(function() { // 默認(rèn)不做任何事
refreshDone();
}, 0);
},
// 加載相關(guān)
noLoad: false, // 關(guān)閉上拉加載特性
bottomHeight: 1, // 距離滾動條底部多少px發(fā)起刷新
onLoad: function (loadDone) {
setTimeout(function() {
loadDone();
}, 0);
},
};
var finalOption = $.extend(true, defaultOption, option);
// 創(chuàng)建iscroll5滾動區(qū)域
var iscroll = new IScroll(container, {
bounce: false,
});
// 關(guān)閉上拉加載特性
if (!finalOption.noLoad) {
// 監(jiān)聽滾動結(jié)束事件,用于上拉加載
iscroll.on('scrollEnd', function () {
// 有滾動條的情況下,才允許上拉加載
if (iscroll.maxScrollY < 0) { // maxScrollY<0表明出現(xiàn)了滾動條
var bottomDistance = (iscroll.maxScrollY - iscroll.y) * -1;
// 距離底部足夠近,觸發(fā)加載
if (bottomDistance <= finalOption.bottomHeight) {
// 當(dāng)前沒有刷新和加載事件正在執(zhí)行
if (!loadEvent && !refreshEvent) {
loadEvent = {}; // 生成新的加載事件
finalOption.onLoad(function (error, msg) {
loadEvent = null; // 清理當(dāng)前的加載事件
// 延遲重繪滾動條
setTimeout(function () {
iscroll.refresh();
}, 0);
});
}
}
}
});
}
// 關(guān)閉下拉刷新特性
if (!finalOption.noRefresh) {
// 緊鄰滾動區(qū)域,容納刷新圖標(biāo)
var pullContainer = $('<div class="pullContainer"></div>')
// 創(chuàng)建小圖標(biāo)
var pullToRefresh = $('<div class="pullToRefresh"><img src="' + finalOption.pullImg + '"></div>');
// 保留小圖標(biāo)的快捷方式
var pullImg = pullToRefresh.find("img");
// 小圖標(biāo)加入到容器
pullContainer.append(pullToRefresh);
// 小圖標(biāo)容器添加到滾動區(qū)域之前
$(container).before(pullContainer);
// 預(yù)加載loadImg
$('<img src="' + finalOption.loadImg + '">');
// 設(shè)置transform的函數(shù)
function cssTransform(node, content) {
node.css({
'-webkit-transform' : content,
'-moz-transform' : content,
'-ms-transform' : content,
'-o-transform' : content,
'transform' : content,
});
}
// 調(diào)整小圖標(biāo)位置,角度,透明度
function goTowards(translateY, rotate, opcaticy) {
// 更新當(dāng)前小圖標(biāo)的位置,獲取css(transform)比較麻煩,所以每次變更時自己保存
curY = translateY;
// 旋轉(zhuǎn)圖標(biāo)(根據(jù)抵達(dá)lowerBound的比例旋轉(zhuǎn),最大轉(zhuǎn)1圈)
if (rotate === undefined) {
rotate = (curY / finalOption.lowerBound) * 360;
}
// 透明度根據(jù)抵達(dá)pauseBound的比例計算
if (opcaticy === undefined) {
opcaticy = (curY / finalOption.pauseBound) * 1;
if (opcaticy > 1) {
opcaticy = 1;
}
}
// 改變位置和旋轉(zhuǎn)角度
cssTransform(pullToRefresh, "translateY(" + translateY + "px) translateZ(0)" + "rotateZ(" + rotate + "deg)");
// 改變透明度
pullToRefresh.css("opacity", opcaticy);
}
// 開啟回彈動畫
function tryStartBackTranTop() {
// 啟動回彈動畫
pullToRefresh.addClass("backTranTop");
// 判斷是否觸發(fā)刷新
if (curY >= finalOption.pauseBound) {
goTowards(finalOption.pauseBound);
// 回彈動畫結(jié)束發(fā)起刷新
pullToRefresh.on('transitionend webkitTransitionEnd oTransitionEnd', function (event) {
// 由于transitionend會對每個屬性回調(diào)一次,所以只處理其中一個
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");
// 回調(diào)刷新數(shù)據(jù),最終應(yīng)將refreshEvent傳回校驗
finalOption.onRefresh(function (error, msg) {
// 用戶回調(diào)時DOM通常已經(jīng)更新, 需要通知iscroll調(diào)整(官方建議延遲執(zhí)行,涉及到瀏覽器重繪問題)
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);
// 恢復(fù)動畫
pullToRefresh.addClass("backTranTop");
// 刷新完成
refreshEvent = null;
// 彈回頂部
goTowards(-55);
}, 100);
});
}
});
} else {
goTowards(-55); // 彈回頂部
refreshEvent = null; // 未達(dá)成刷新觸發(fā)條件
}
}
// 父容器注冊下拉事件
$(container).on("touchstart", function (event) {
// 新的觸摸事件
touchEvent = {};
// 有一個刷新事件正在進(jìn)行
if (refreshEvent) {
return;
}
// 只有滾動軸位置接近頂部, 才可以生成新的刷新事件
if (iscroll.y < -1 * finalOption.lowerBound) {
return;
}
// 一個新的刷新事件
refreshEvent = touchEvent;
touchStartY = event.originalEvent.changedTouches[0].clientY;
pullStartY = curY;
// 如果存在,則關(guān)閉回彈動畫與相關(guān)監(jiān)聽
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; // 計算圖標(biāo)應(yīng)該移動到的位置
// 向下不能拉出范圍
if (curPullY > finalOption.lowerBound) {
curPullY = finalOption.lowerBound;
}
// 向上不能拉出范圍
if (curPullY <= -55) {
curPullY = -55;
}
// 更新圖標(biāo)的位置
goTowards(curPullY);
}).on("touchend", function (event) {
// 在刷新未完成前觸摸,將被忽略
if (touchEvent != refreshEvent) {
return;
}
// 嘗試啟動回彈動畫
tryStartBackTranTop();
});
}
// 初始化iscroll
setTimeout(function() {
iscroll.refresh();
}, 0);
// 返回操作此區(qū)域的工具對象
return {
// 用戶如果在下拉刷新之外修改了滾動區(qū)域的內(nèi)容,需要主動調(diào)用refresh
refresh: function() {
// 延遲以便配合瀏覽器重繪
setTimeout(function() {
iscroll.refresh();
}, 0);
},
// 觸發(fā)下拉刷新
triggerPull: function() {
// 正在刷新或者禁止刷新
if (refreshEvent || finalOption.noRefresh) {
return false;
}
// 暫??赡苷谶M(jìn)行的最終階段回彈動畫
pullToRefresh.removeClass("backTranTop");
// 小圖標(biāo)移動到lowerbound位置
goTowards(finalOption.lowerBound);
// 創(chuàng)建新的刷新事件,占坑可以阻止在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旋轉(zhuǎn)動畫 */
.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實現(xiàn)下拉刷新/上拉加載插件,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
JavaScript實現(xiàn)的商品搶購倒計時功能示例
這篇文章主要介紹了JavaScript實現(xiàn)的商品搶購倒計時功能,可實現(xiàn)分秒級別的實時顯示倒計時效果,涉及js日期時間計算與頁面元素動態(tài)操作相關(guān)技巧,需要的朋友可以參考下2017-04-04
javascript進(jìn)行數(shù)組追加方法小結(jié)
javascript中給數(shù)組加元素是一個非常簡單的問題,javascript本身就提供了大量這類函數(shù),我們可以使用js自帶函數(shù)快速給數(shù)組增加元素了,本文就javascript進(jìn)行數(shù)組追加的方法做出如下小結(jié)。2014-06-06
JS 通過系統(tǒng)時間限定動態(tài)添加 select option的實例代碼
這篇文章主要介紹了JS 通過系統(tǒng)時間限定 動態(tài)添加 select option的實例代碼,非常不錯具有參考借鑒價值,需要的朋友可以參考下2016-06-06

