@studiometa/js-toolkit
Version:
A set of useful little bits of JavaScript to boost your project! 🚀
112 lines (111 loc) • 3.7 kB
JavaScript
import { tween } from "./tween.js";
import { isString, isNumber, isFunction, isDefined } from "./is.js";
import { lerp } from "./math/index.js";
const xAxis = Symbol(0);
const yAxis = Symbol(1);
const bothAxis = Symbol(2);
function isHtmlElement(rootElement) {
return rootElement instanceof HTMLElement;
}
function getCurrentScrollPosition(rootElement) {
return {
left: isHtmlElement(rootElement) ? rootElement.scrollTop : rootElement.scrollX,
top: isHtmlElement(rootElement) ? rootElement.scrollLeft : rootElement.scrollY
};
}
function getMaxScrollPosition(rootElement) {
return {
left: isHtmlElement(rootElement) ? rootElement.scrollWidth - rootElement.offsetWidth : document.documentElement.scrollWidth - window.innerWidth,
top: isHtmlElement(rootElement) ? rootElement.scrollHeight - rootElement.offsetHeight : document.documentElement.scrollHeight - window.innerHeight
};
}
function getTargetScrollPosition(initialScrollPosition, maxScrollPosition, target, axis, offset) {
let { top, left } = initialScrollPosition;
if (target instanceof HTMLElement || isString(target)) {
const targetElement = target instanceof HTMLElement ? target : document.querySelector(target);
if (!targetElement) {
return { top, left };
}
const sizes = targetElement.getBoundingClientRect();
const styles = getComputedStyle(targetElement);
const scrollMargin = {
left: Number.parseInt(styles.scrollMarginLeft || "0", 10),
top: Number.parseInt(styles.scrollMarginTop || "0", 10)
};
left = sizes.left + initialScrollPosition.left - scrollMargin.left;
top = sizes.top + initialScrollPosition.top - scrollMargin.top;
} else if (isNumber(target)) {
left = target;
top = target;
} else {
left = target.left ?? left;
top = target.top ?? top;
}
left -= offset;
top -= offset;
if (axis !== bothAxis) {
if (axis !== xAxis && !isDefined(target.left)) {
left = initialScrollPosition.left;
}
if (axis !== yAxis) {
top = initialScrollPosition.top;
}
}
return {
left: Math.min(left, maxScrollPosition.left),
top: Math.min(top, maxScrollPosition.top)
};
}
function scrollTo(target, { rootElement = window, axis = yAxis, offset = 0, ...tweenOptions } = {}) {
const initialScrollPosition = getCurrentScrollPosition(rootElement);
const maxScrollPosition = getMaxScrollPosition(rootElement);
const targetScrollPosition = getTargetScrollPosition(
initialScrollPosition,
maxScrollPosition,
target,
axis,
offset
);
return new Promise((resolve) => {
function eventHandler() {
tw.pause();
stop();
}
function stop() {
rootElement.removeEventListener("wheel", eventHandler);
rootElement.removeEventListener("touchmove", eventHandler);
resolve(getCurrentScrollPosition(rootElement));
}
const tw = tween(
(progress) => {
rootElement.scrollTo({
left: lerp(initialScrollPosition.left, targetScrollPosition.left, progress),
top: lerp(initialScrollPosition.top, targetScrollPosition.top, progress),
behavior: "instant"
});
},
{
smooth: 0.2,
...tweenOptions,
onFinish(progress) {
if (isFunction(tweenOptions.onFinish)) {
tweenOptions.onFinish(progress);
}
stop();
}
}
);
rootElement.addEventListener("wheel", eventHandler, { passive: true });
rootElement.addEventListener("touchmove", eventHandler, { passive: true });
tw.start();
});
}
scrollTo.axis = {
x: xAxis,
y: yAxis,
both: bothAxis
};
export {
scrollTo
};
//# sourceMappingURL=scrollTo.js.map