You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

79 lines
2.3 KiB
JavaScript

const sectionsContainer = document.querySelector('.page-sections');
const sections = document.querySelectorAll('.page-section');
const nav = document.querySelector('.nav-sections');
const menu = nav.querySelector('.menu');
const links = nav.querySelectorAll('.menu-item-link');
const activeLine = nav.querySelector('.active-line');
const sectionOffset = nav.offsetHeight + 24;
const activeClass = 'active';
let activeIndex = 0;
let isScrolling = true;
let userScroll = true;
const setActiveClass = () => {
links[activeIndex].classList.add(activeClass);
};
const removeActiveClass = () => {
links[activeIndex].classList.remove(activeClass);
};
const moveActiveLine = () => {
const link = links[activeIndex];
const linkX = link.getBoundingClientRect().x;
const menuX = menu.getBoundingClientRect().x;
activeLine.style.transform = `translateX(${menu.scrollLeft - menuX + linkX}px)`;
activeLine.style.width = `${link.offsetWidth}px`;
};
const setMenuLeftPosition = position => {
menu.scrollTo({
left: position,
behavior: 'smooth' });
};
const checkMenuOverflow = () => {
const activeLink = links[activeIndex].getBoundingClientRect();
const offset = 30;
if (Math.floor(activeLink.right) > window.innerWidth) {
setMenuLeftPosition(menu.scrollLeft + activeLink.right - window.innerWidth + offset);
} else if (activeLink.left < 0) {
setMenuLeftPosition(menu.scrollLeft + activeLink.left - offset);
}
};
const handleActiveLinkUpdate = current => {
removeActiveClass();
activeIndex = current;
checkMenuOverflow();
setActiveClass();
moveActiveLine();
};
const init = () => {
moveActiveLine(links[0]);
document.documentElement.style.setProperty('--section-offset', sectionOffset);
};
links.forEach((link, index) => link.addEventListener('click', () => {
userScroll = false;
handleActiveLinkUpdate(index);
}));
window.addEventListener("scroll", () => {
const currentIndex = sectionsContainer.getBoundingClientRect().top < 0 ?
sections.length - 1 - [...sections].reverse().findIndex(section => window.scrollY >= section.offsetTop - sectionOffset * 2) :
0;
if (userScroll && activeIndex !== currentIndex) {
handleActiveLinkUpdate(currentIndex);
} else {
window.clearTimeout(isScrolling);
isScrolling = setTimeout(() => userScroll = true, 100);
}
});
init();