原文連結:justrockit.top
前言
在移動端H5網頁中,下拉重新整理和上拉載入更多資料的互動方式出現頻率很高,開源社群也有很多類似的解決方案,如iscroll,pulltorefresh.js庫等。下面是對這兩種常見互動基本實現原理的闡述。
實現原理
下拉重新整理
實現下拉重新整理主要分為三步:
- 監聽原生
touchstart
事件,記錄其初始位置的值,e.touches[0].pageY
; - 監聽原生
touchmove
事件,記錄並計算當前滑動的位置值與初始位置值的差值,大於0
表示向下拉動,並藉助CSS3的translateY
屬性使元素跟隨手勢向下滑動對應的差值,同時也應設定一個允許滑動的最大值; - 監聽原生
touchend
事件,若此時元素滑動達到最大值,則觸發callback
,同時將translateY
重設為0
,元素回到初始位置。
示例。檢視連結:下拉重新整理demo(PC瀏覽器需調成手機模擬器模式檢視)
html
<main>
<p class="refreshText"></p>
<ul id="refreshContainer">
<li>111</li>
<li>222</li>
<li>333</li>
<li>444</li>
<li>555</li>
...
</ul>
</main>
javascript
(function(window) {
var _element = document.getElementById(`refreshContainer`),
_refreshText = document.querySelector(`.refreshText`),
_startPos = 0,
_transitionHeight = 0;
_element.addEventListener(`touchstart`, function(e) {
console.log(`初始位置:`, e.touches[0].pageY);
_startPos = e.touches[0].pageY;
_element.style.position = `relative`;
_element.style.transition = `transform 0s`;
}, false);
_element.addEventListener(`touchmove`, function(e) {
console.log(`當前位置:`, e.touches[0].pageY);
_transitionHeight = e.touches[0].pageY - _startPos;
if (_transitionHeight > 0 && _transitionHeight < 60) {
_refreshText.innerText = `下拉重新整理`;
_element.style.transform = `translateY(`+_transitionHeight+`px)`;
if (_transitionHeight > 55) {
_refreshText.innerText = `釋放更新`;
}
}
}, false);
_element.addEventListener(`touchend`, function(e) {
_element.style.transition = `transform 0.5s ease 1s`;
_element.style.transform = `translateY(0px)`;
_refreshText.innerText = `更新中...`;
// todo...
}, false);
})(window);
在下拉到鬆手的過程中,經歷了三個狀態:
- 當前手勢滑動位置與初始位置差值大於零時,提示正在進行下拉重新整理操作;
- 下拉到一定值時,顯示鬆手釋放後的操作提示;
- 下拉到達設定最大值鬆手時,執行回撥,提示正在進行更新操作。
上拉載入
上拉載入更多資料是在頁面滾動時觸發的動作,一般是頁面滾動到底部時觸發,也可以選擇在滾動到一定位置的時候觸發。
以滾動到頁面底部為例。實現原理是分別獲得當前滾動條的scrollTop
值、當前可視範圍的高度值clientHeight
以及文件的總高度scrollHeight
。當scrollTop
和clientHeight
的值之和大於等於scrollHeight
時,觸發callback
。
示例。檢視連結:上拉載入demo
html
<main>
<ul id="refreshContainer">
<li>111</li>
<li>222</li>
<li>333</li>
<li>444</li>
<li>555</li>
...
</ul>
<p class="refreshText"></p>
</main>
(function(window) {
// 獲取當前滾動條的位置
function getScrollTop() {
var scrollTop = 0;
if (document.documentElement && document.documentElement.scrollTop) {
scrollTop = document.documentElement.scrollTop;
} else if (document.body) {
scrollTop = document.body.scrollTop;
}
return scrollTop;
}
// 獲取當前可視範圍的高度
function getClientHeight() {
var clientHeight = 0;
if (document.body.clientHeight && document.documentElement.clientHeight) {
clientHeight = Math.min(document.body.clientHeight, document.documentElement.clientHeight);
}
else {
clientHeight = Math.max(document.body.clientHeight, document.documentElement.clientHeight);
}
return clientHeight;
}
// 獲取文件完整的高度
function getScrollHeight() {
return Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
}
var _text = document.querySelector(`.refreshText`),
_container = document.getElementById(`refreshContainer`);
// 節流函式
var throttle = function(method, context){
clearTimeout(method.tId);
method.tId = setTimeout(function(){
method.call(context);
}, 300);
}
function fetchData() {
setTimeout(function() {
_container.insertAdjacentHTML(`beforeend`, `<li>new add...</li>`);
}, 1000);
}
window.onscroll = function() {
if (getScrollTop() + getClientHeight() == getScrollHeight()) {
_text.innerText = `載入中...`;
throttle(fetchData);
}
};
})(window);
頁面繫結onscroll
事件時加入了節流函式,其作用就是忽略滾動條300毫秒內的連續多次觸發。
小結
上拉重新整理的實現主要依靠的是touch
事件的三個階段,以及藉助CSS3動畫效果;下拉載入主要依靠頁面的onscroll
事件,需要注意的是頁面滾動時可能要考慮函式節流。