效果展示
掃碼體驗
背景
下拉重新整理是移動端經常會用到的功能,看了各種各樣的ui庫,自己想搗鼓一下,研究下其中的原理,於是便有了這個例子。
頁面骨架
<template>
<div class="pullDown">
<!--loading-->
<div class="pullDown__loading"></div>
<!--scroll content-->
<div class="pullDown__scroll">
<ul class="pullDown__list"></ul>
</div>
</div>
</template>
複製程式碼
頁面基本樣式
.pullDown{
background-color: #fff;
position: absolute;
z-index: 1;
width: 100%;
height: 100%;
overflow-y: scroll;
/* 增加該屬性,可以增加彈性 */
-webkit-overflow-scrolling: touch;
transform: translate3d(0, 0, 0);
-webkit-transform: translate3d(0, 0, 0);
}
.pullDonw__loading{
position: absolute;
left: 0;
top: 0;
display: block;
overflow: hidden;
width: 100%;
height: 50px;
text-align: center;
padding: 16px 24px;
z-index: 2;
background-color: #fff;
}
.pullDown__scroll{
position: relative;
z-index: 10;
background-color: #fff;
border-bottom: 1px solid #e8e8e8;
border-top: 1px solid #e8e8e8;
}
複製程式碼
核心實現
下拉重新整理主要根據touchstart,touchmove,touchend三個事件進行判斷操作。
- touchstar觸發時,則判斷重新整理狀態中和容器到達頂端的情況,否則記錄當前位置資訊並設定可拉動狀態;
- touchemove觸發時,判斷重新整理狀態中和可拉動狀態,且當前移動位置需大於開始位置,則設定容器跟隨手指滾動;
- touchend觸發時,判斷重新整理狀態中和動畫中,分兩種情況:
- 拉動距離超過重新整理情況,則執行動畫,重新整理動作
- 拉動距離沒超過重新整理情況,則返回原始狀態
touchstart($event) {
var vm= this;
//判斷是否允許下拉
if (!vm.isPull && !vm.isLoading && vm._scrollWrap.scrollTop == vm.offsetTop) {
vm.isPull= true;
vm.startPos= $event.touches[0].pageY;
};
},
touchmove($event) {
var vm= this;
vm.endPos= $event.touches[0].pageY;
//是否能拉動
if (!vm.isLoading && vm.isPull && vm.startPos<vm.endPos) {
vm.distance= (vm.endPos- vm.startPos)*vm.modulus;
var offsetLoading= vm._loadingWrap.offsetHeight;
if (vm.distance >= offsetLoading) {
vm.pullStatus = 'up'
}else{
vm.pullStatus = 'down'
}
vm.moveTransition(vm.$refs.scroll, vm.distance, 0);
}
},
touchend($event) {
var vm= this;
var offsetLoading= vm._loadingWrap.offsetHeight;
// vm.isPull= false;
//載入中 或 移動距離小於0不能移動
if (vm.isLoading || vm.distance<=0 || vm.isBack) {return false;}
//拉動距離大於臨界值
if ( vm.distance>0 && vm.distance >= offsetLoading) {
//執行重新整理動作
vm.pullTransition({
dom: vm._scrollWrap,
begin: offsetLoading,
end: vm.distance,
duration: 500
});
//後臺重新整理資料
vm.refresh();
}else{
vm.isBack= true;
// 返回back初始狀態
vm.pullTransition({
dom: vm._scrollWrap,
end: vm.distance,
duration: 500,
callback: vm.resetStatus
});
}
}
複製程式碼
下拉動畫實現
採用css動畫,則難以精準把握動畫結束的時間。故採用RequestAnimationFrame實現動畫,可監聽到結束狀態並處理回撥。有關 requestAnimationFrame參考以下資料:
/**
* [pullTransition 動畫拉動]
* @param {[type]} dom [元素]
* @param {[type]} begin [起始值]
* @param {[type]} end [結束值]
* @param {[type]} duration [時間]
* @param {[type]} callback [回撥函式]
* @return {[type]} [description]
*/
pullTransition(opt) {
var vm= this;
if (!opt.dom || !opt.end) {
console.error('引數不足');
return false;
};
var opt= {
dom: opt.dom,
currentTime: 0,
begin: opt.begin? opt.begin: 0,
end: opt.end,
//相當一部分的瀏覽器的顯示頻率是16.7ms 約等於17
duration: opt.duration? Math.ceil(opt.duration / 17) : 0,
callback: opt.callback || false
};
var step= function() {
//根據緩動演算法取得值
var value = opt.end-vm.easeInOut(opt.currentTime, opt.begin, opt.end-opt.begin, opt.duration)+opt.begin;
opt.dom.style.transform= 'translate3d(0,'+value+'px,0)';
opt.currentTime++;
//判斷是否到時間
if (opt.currentTime <= opt.duration) {
// 繼續運動
requestAnimationFrame(step);
} else {
// 動畫結束
if(opt.callback) opt.callback();
}
};
step();
}
/**
* [Linear 緩動演算法]
* @param {[type]} t [current time(當前時間)]
* @param {[type]} b [beginning value(初始值)]
* @param {[type]} c [change in value(變化量)]
* @param {[type]} d [duration(持續時間)]
*/
easeInOut(t, b, c, d) {
if ((t /= d / 2) < 1) return c / 2 * t * t*t + b;
return c / 2*((t -= 2) * t * t + 2) + b;
}
複製程式碼
微信下拉顯示網址處理
在微信開啟一個頁面時,在頂部用力往下拉的時候會出現如下圖情況,對於下拉重新整理來說體驗比較差。我在網上搜尋了下找到兩種方式處理,供各位參考。本例子採用第一種方式,至於採用的依據目前還未深入比較,後續再深入調查。
至此,下拉重新整理功能完成,任何意見和建議歡迎提出。