in src/focus.service.ts [446:539]
private rescroll(el: HTMLElement, scrollSpeed: number | null, container: HTMLElement) {
// Abort if scrolling is disabled.
if (scrollSpeed === null) {
return;
}
// Animation function to transition a scroll on the `parent` from the
// `original` value to the `target` value by calling `set.
const animate = (
parentElement: HTMLElement,
target: number,
original: number,
setter: (x: number) => void,
) => {
if (scrollSpeed === Infinity) {
parentElement.scrollTop = target;
return;
}
const start = performance.now();
const duration = Math.abs(target - original) / scrollSpeed * 1000;
const run = (now: number) => {
const progress = Math.min((now - start) / duration, 1);
setter(quad(original, target, progress));
if (progress < 1) {
requestAnimationFrame(run);
}
};
requestAnimationFrame(run);
};
// The scroll calculation loop. Starts at the element and goes up, ensuring
// that the element (or the box where the element will be after scrolling
// is applied) is visible in all containers.
const { width, height, top, left } = this.referenceRect;
let parent = el.parentElement;
while (parent != null && parent !== container.parentElement) {
// Special case: treat the body as the viewport as far as scrolling goes.
let prect: IReducedClientRect;
if (parent === container) {
const containerStyle = window.getComputedStyle(container, undefined);
const paddingTop = containerStyle.paddingTop
? Number(containerStyle.paddingTop.slice(0, -2))
: 0;
const paddingBottom = containerStyle.paddingBottom
? Number(containerStyle.paddingBottom.slice(0, -2))
: 0;
const paddingLeft = containerStyle.paddingLeft
? Number(containerStyle.paddingLeft.slice(0, -2))
: 0;
const paddingRight = containerStyle.paddingRight
? Number(containerStyle.paddingRight.slice(0, -2))
: 0;
prect = {
top: paddingTop,
left: paddingLeft,
height: container.clientHeight - paddingTop - paddingBottom,
width: container.clientWidth - paddingLeft - paddingRight,
};
} else {
prect = parent.getBoundingClientRect();
}
// Trigger if this element has a vertical scrollbar
if (parent.scrollHeight > parent.clientHeight) {
const scrollTop = parent.scrollTop;
const showsTop = scrollTop + (top - prect.top);
const showsBottom = showsTop + (height - prect.height);
if (showsTop < scrollTop) {
animate(parent, showsTop, scrollTop, x => ((<HTMLElement>parent).scrollTop = x));
} else if (showsBottom > scrollTop) {
animate(parent, showsBottom, scrollTop, x => ((<HTMLElement>parent).scrollTop = x));
}
}
// Trigger if this element has a horizontal scrollbar
if (parent.scrollWidth > parent.clientWidth) {
const scrollLeft = parent.scrollLeft;
const showsLeft = scrollLeft + (left - prect.left);
const showsRight = showsLeft + (width - prect.width);
if (showsLeft < scrollLeft) {
animate(parent, showsLeft, scrollLeft, x => ((<HTMLElement>parent).scrollLeft = x));
} else if (showsRight > scrollLeft) {
animate(parent, showsRight, scrollLeft, x => ((<HTMLElement>parent).scrollLeft = x));
}
}
parent = parent.parentElement;
}
}