web-ui-pack
Version:
Web package with UI elements
52 lines (51 loc) • 2.13 kB
JavaScript
import findScrollParent from "./findScrollParent";
import isIntoView from "./isIntoView";
export default function scrollIntoView(el, options) {
const opts = { smoothMs: 400, offsetTop: -30, offsetLeft: -30, onlyIfNeeded: true, ...options };
const p = findScrollParent(el);
if (!p) {
return Promise.resolve();
}
const oTop = opts.offsetTop ?? 0;
const oLeft = opts.offsetLeft ?? 0;
const viewResult = isIntoView(el, { scrollParents: [p], offset: [-oTop, -oLeft] });
if (opts.onlyIfNeeded && viewResult.visible) {
return Promise.resolve();
}
const elRect = el.getBoundingClientRect();
const pRect = p.getBoundingClientRect();
const diffTop = elRect.top - pRect.top + oTop;
const diffLeft = elRect.left - pRect.left + oLeft;
if (!opts.smoothMs) {
p.scrollBy({ top: diffTop, left: diffLeft, behavior: "auto" });
return Promise.resolve();
}
const fromTop = p.scrollTop;
const fromLeft = p.scrollLeft;
const needY = viewResult.hiddenY || viewResult.partialHiddenY;
const needX = viewResult.hiddenX || viewResult.partialHiddenX;
const scrollTopEnd = p.scrollHeight - p.offsetHeight;
const toTop = Math.min(scrollTopEnd, Math.max(0, fromTop + diffTop));
const scrollLeftEnd = p.scrollWidth - p.offsetWidth;
const toLeft = Math.min(scrollLeftEnd, Math.max(0, fromLeft + diffLeft));
const animTime = opts.smoothMs;
return new Promise((resolve) => {
let start = 0;
const animate = (t) => {
if (!start) {
start = t;
}
const cur = Math.min(t - start, animTime);
const v = cur / animTime;
const resTop = fromTop + (toTop - fromTop) * v;
const resLeft = fromLeft + (toLeft - fromLeft) * v;
p.scrollTo({ top: needY ? resTop : undefined, left: needX ? resLeft : undefined });
if (cur === animTime) {
resolve();
return;
}
window.requestAnimationFrame(animate);
};
window.requestAnimationFrame(animate);
});
}