學習vue-element-admin這個項目的table組件的時候,點擊分頁會主動滾動到最上面,方便查看。以爲這個功能挺不錯,就去研究了一下它的實現方式。vue
源碼位置位於src/utils/scroll-to.js
,對外暴露了一個scrollTo方法。git
/** * @param {number} to 滾動到位置 * @param {number} duration 滾動動畫持續時間 * @param {Function} callback 滾動結束回調函數 */
export function scrollTo(to, duration, callback) {
// todo
}
複製代碼
requestAnimationFrame告訴瀏覽器——你但願執行一個動畫,而且要求瀏覽器在下次重繪以前調用指定的回調函數更新動畫。該方法須要傳入一個回調函數做爲參數,該回調函數會在瀏覽器下一次重繪以前執行。而且改回調函數執行此時一般與瀏覽器屏幕刷新次數相匹配。一般爲每秒60次。github
優勢:web
在來看標題scroll平滑滾動到指定位置
,天然而然就能想到,在requestAnimationFrame的回調的回調函數中一次次的修改一下滾動的位置,就能夠實現一個平滑的scroll滾動了。查看了一下element-ui
的Backtop
組件中也是使用這個api來實現的。element-ui
Element.scrollTop 屬性能夠獲取或設置一個元素的內容垂直滾動的像素數。api
const easeInOutQuad = (t: number, b: number, c: number, d: number) => {
t /= d / 2;
if (t < 1) {
return (c / 2) * t * t + b;
}
t--;
return (-c / 2) * (t * (t - 2) - 1) + b;
};
// 最後面哪一個setTimeout是爲了兼容不支持requestAnimationFrame的瀏覽器。
const requestAnimFrame = (function() {
return (
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
(window as any).mozRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
}
);
})();
// 這裏不太好檢測滾動是哪一個元素,因此乾脆都移動了
function move(amount: number) {
document.documentElement.scrollTop = amount;
(document.body.parentNode as HTMLElement).scrollTop = amount;
document.body.scrollTop = amount;
}
function position() {
return (
document.documentElement.scrollTop ||
(document.body.parentNode as HTMLElement).scrollTop ||
document.body.scrollTop
);
}
export function scrollTo(to: number, duration: number, callback?: Function) {
// 開始滾動的位置
const start = position();
// 須要滾動的距離
const change = to - start;
// 當前時間
let currentTime = 0;
// 每次增加時間
// 注:vue-element-admin中這個值是20,若是不考慮兼容性,我我的以爲應該設置爲17毫秒,由於按照requestAnimationFrame回調函數每秒執行60此來算,每次花費16.666666毫秒,猜想設置爲20毫秒是爲了防止setTimeout出現延時形成問題吧。
const increment = 20;
// 持續時間,默認500毫秒
duration = typeof duration === "undefined" ? 500 : duration;
const animateScroll = function() {
currentTime += increment;
// 計算移動的距離
const val = easeInOutQuad(currentTime, start, change, duration);
// 移動
move(val);
if (currentTime < duration) {
// 遞歸調用
requestAnimFrame(animateScroll);
}
};
animateScroll();
}
複製代碼