学习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();
}
复制代码