原理如下:
假設要從數值A變化到數值B,如果是線性運動,則每次移動距離是一樣;如果是緩動,每次移動距離不一樣。那如何才能不一樣呢?很簡單,按比例移動就可以。
例如:每次移動剩余距離的一半。
對吧,超容易理解的。
比方說:你和初戀之間距離是64,每秒移動一半,則,你們之間的距離下一秒就是32, 再下一秒就是16,然后8,然后4,然后2,然后1,然后……你們就在一起了。你們在一起的這個過程就是一個典型的先快后慢的緩動運動過程,如下示意圖:
用一個簡單的公式表示就是:
A = A + (B - A) / 2
點擊按鈕執行的是下面的backToTop()
方法:
// 滾動到頂部緩動實現 // rate表示緩動速率,默認是2 var backToTop = function (rate) { var doc = document.body.scrollTop? document.body : document.documentElement; var scrollTop = doc.scrollTop; var top = function () { scrollTop = scrollTop + (0 - scrollTop) / (rate || 2); // 臨界判斷,終止動畫 if (scrollTop < 1) { doc.scrollTop = 0; return; } doc.scrollTop = scrollTop; // 動畫gogogo! requestAnimationFrame(top); }; top(); };
其中,代碼的核心是:
scrollTop = scrollTop + (0 - scrollTop) / (rate || 2);
scrollTop
表示公式的A, 滾動到頂部滾動高度是0
,因此,上面的0
,實際上就是公式的B, 而公式中的2
表示緩動速率,實際開發的時候是可以靈活調整的,緩動速率范圍是1
到無窮大,速率值越小,運動越快。比如說上面的返回頂部效果,我們把緩動速率改成4
,點擊下面的按鈕感受效果:
等比例靠近理論上最終只會無窮靠近,並不會真正的相等,也就是動畫永遠沒有結束的時候,所以說需要做一個臨界判斷,也就是距離小到一定數目的時候,直接等於目標值,並終止動畫。例如,上面的返回頂部,就是當距離頂部滾動高度小於1
的時候,直接返回頂部,並終止動畫。
if (scrollTop < 1) { doc.scrollTop = 0; return; }
如果項目很多地方使用該算法,每次都寫一遍requestAnimationFrame
和邊界判斷是很啰嗦的,於是,我們可以把算法變個身,例如下面這樣:
var _easeout = function(start, end, rate, callback) {
var _end = end;
if (start == end || typeof start != 'number') {
return;
}
end = end || 0;
rate = rate || 2;
var step = function() {
start = start + (end - start) / rate;
if (Math.abs(start - _end) < 1) {
console.log('end');
callback(end, true);
return;
}
callback(start, false);
requestAnimationFrame(step);
};
step();
};
其中:
A
是起始位置;B
是目標位置;rate
是緩動速率;callback
是變化的位置回調,支持兩個參數,value
和isEnding
,表示當前的位置值(數值)以及是否動畫結束了(布爾值);
於是,我們的返回頂部效果可以這么使用:
var doc = document.body.scrollTop !== undefined ? document.body : document.documentElement;
console.log('start', doc.scrollTop);
$("#back").click(function(){
console.log('start', doc.scrollTop);
_easeout(doc.scrollTop, 500, 2, function(value) {
doc.scrollTop = value;
});
})
});