Animated scroll to target element inside window or relative scrollable element
/**
* Animated scroll to target element inside window
* or relative scrollable element
*
* @param {Object} options
* @param {Element} options.targetElement
* @param {Number} options.duration
* @param {Element} [options.insideElement=window]
* @param {Number} [options.offset=0]
* @param {Function} [options.onStart]
* @param {Function} [options.onEnd]
*/
function animatedScrollTo({targetElement, duration, insideElement = window, offset = 0, onStart, onEnd}) {
const startPositionY = insideElement.scrollTop;
const targetPositionY = startPositionY + targetElement.getBoundingClientRect().top + offset;
if (targetPositionY === startPositionY) {
return;
}
let currentPositionY = 0;
let start;
if (typeof onStart === 'function') {
onStart();
}
window.requestAnimationFrame(animateScroll);
function animateScroll(timestamp) {
start = start || timestamp;
const deltaTop = Math.round(targetPositionY - startPositionY);
const progress = timestamp - start;
const percent = (progress >= duration ? 1 : easeInOutQuad(progress / duration));
currentPositionY = startPositionY + Math.ceil(deltaTop * percent);
insideElement.scrollTop = currentPositionY;
if (percent < 1) {
window.requestAnimationFrame(animateScroll);
} else if (typeof onStart === 'function') {
onEnd();
}
}
}
function easeInOutQuad(x) {
return x < 0.5 ? 8 * x * x * x * x : 1 - 8 * (--x) * x * x * x;
}
export default animatedScrollTo;