UNPKG

@studiometa/js-toolkit

Version:

A set of useful little bits of JavaScript to boost your project! 🚀

112 lines (111 loc) • 3.7 kB
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