You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
151 lines
4.5 KiB
151 lines
4.5 KiB
import "./common.js";
|
|
//#region js/about-history.js
|
|
function setActiveNav() {
|
|
const pageNav = document.querySelector("[data-nav]")?.getAttribute("data-nav");
|
|
if (!pageNav) return;
|
|
document.querySelectorAll(".site-header__nav a").forEach((link) => {
|
|
const active = link.textContent.trim().startsWith(pageNav);
|
|
link.classList.toggle("is-active", active);
|
|
});
|
|
}
|
|
|
|
function getHeaderOffset() {
|
|
const header = document.querySelector(".site-header");
|
|
const h = header ? header.offsetHeight : 88;
|
|
const isMobile = window.matchMedia("(max-width: 992px)").matches;
|
|
if (isMobile) {
|
|
const years = document.querySelector(".ah-nav__years");
|
|
const yearsH = years ? years.offsetHeight : 60;
|
|
return h + yearsH + 12;
|
|
}
|
|
return h + 24;
|
|
}
|
|
|
|
function initHistorySpy() {
|
|
const root = document.querySelector("[data-ah-spy]");
|
|
if (!root) return;
|
|
const navItems = Array.from(root.querySelectorAll(".ah-nav__item[data-year]"));
|
|
const blocks = Array.from(root.querySelectorAll(".ah-block[data-year]"));
|
|
const yearsWrap = root.querySelector(".ah-nav__years");
|
|
const rail = root.querySelector(".ah-nav__rail");
|
|
const indicator = root.querySelector(".ah-nav__indicator");
|
|
if (!navItems.length || !blocks.length) return;
|
|
|
|
let activeYear = navItems[0].dataset.year;
|
|
let clicking = false;
|
|
|
|
const syncRailHeight = () => {
|
|
if (!yearsWrap || !rail) return;
|
|
rail.style.height = `${yearsWrap.offsetHeight}px`;
|
|
};
|
|
|
|
const updateLine = (item) => {
|
|
if (!indicator || !item || !yearsWrap) return;
|
|
syncRailHeight();
|
|
const itemRect = item.getBoundingClientRect();
|
|
const yearsRect = yearsWrap.getBoundingClientRect();
|
|
// 原点固定在顶部,线条延伸到当前高亮年份底部
|
|
const height = Math.max(itemRect.bottom - yearsRect.top, 8);
|
|
indicator.style.height = `${height}px`;
|
|
indicator.style.transform = "";
|
|
};
|
|
|
|
const updateLineSoon = (item) => {
|
|
updateLine(item);
|
|
requestAnimationFrame(() => updateLine(item));
|
|
window.setTimeout(() => updateLine(item), 320);
|
|
};
|
|
|
|
const setActive = (year) => {
|
|
if (!year || year === activeYear) {
|
|
const current = navItems.find((el) => el.dataset.year === activeYear);
|
|
updateLineSoon(current);
|
|
return;
|
|
}
|
|
activeYear = year;
|
|
navItems.forEach((el) => {
|
|
el.classList.toggle("is-active", el.dataset.year === year);
|
|
});
|
|
const current = navItems.find((el) => el.dataset.year === year);
|
|
updateLineSoon(current);
|
|
if (current && current.closest(".ah-nav__years") && window.matchMedia("(max-width: 992px)").matches) {
|
|
current.scrollIntoView({
|
|
inline: "center",
|
|
block: "nearest",
|
|
behavior: "smooth"
|
|
});
|
|
}
|
|
};
|
|
|
|
const scrollToYear = (year) => {
|
|
const target = blocks.find((el) => el.dataset.year === year);
|
|
if (!target) return;
|
|
clicking = true;
|
|
setActive(year);
|
|
const top = window.scrollY + target.getBoundingClientRect().top - getHeaderOffset();
|
|
const done = () => {
|
|
clicking = false;
|
|
};
|
|
if (window.lenis) {
|
|
window.lenis.scrollTo(top, {
|
|
duration: 1,
|
|
onComplete: done
|
|
});
|
|
} else {
|
|
window.scrollTo({
|
|
top,
|
|
behavior: "smooth"
|
|
});
|
|
window.setTimeout(done, 800);
|
|
}
|
|
};
|
|
|
|
navItems.forEach((item) => {
|
|
item.addEventListener("click", (event) => {
|
|
event.preventDefault();
|
|
scrollToYear(item.dataset.year);
|
|
});
|
|
});
|
|
|
|
const io = new IntersectionObserver((entries) => {
|
|
if (clicking) return;
|
|
const visible = entries.filter((e) => e.isIntersecting).sort((a, b) => b.intersectionRatio - a.intersectionRatio);
|
|
if (!visible.length) return;
|
|
const year = visible[0].target.getAttribute("data-year");
|
|
if (year) setActive(year);
|
|
}, {
|
|
root: null,
|
|
rootMargin: `-${getHeaderOffset()}px 0px -45% 0px`,
|
|
threshold: [0.05, 0.2, 0.4, 0.6]
|
|
});
|
|
|
|
blocks.forEach((block) => io.observe(block));
|
|
|
|
const onScrollFallback = () => {
|
|
if (clicking) return;
|
|
const offset = getHeaderOffset() + 8;
|
|
let current = blocks[0]?.dataset.year;
|
|
for (const block of blocks) {
|
|
if (block.getBoundingClientRect().top <= offset) current = block.dataset.year;
|
|
}
|
|
if (current) setActive(current);
|
|
};
|
|
|
|
if (window.lenis) window.lenis.on("scroll", onScrollFallback);
|
|
else window.addEventListener("scroll", onScrollFallback, { passive: true });
|
|
|
|
setActive(activeYear);
|
|
window.addEventListener("resize", () => {
|
|
const current = navItems.find((el) => el.dataset.year === activeYear);
|
|
updateLineSoon(current);
|
|
});
|
|
|
|
const hashYear = (location.hash || "").replace(/^#year-/, "");
|
|
if (hashYear && blocks.some((el) => el.dataset.year === hashYear)) {
|
|
requestAnimationFrame(() => scrollToYear(hashYear));
|
|
}
|
|
}
|
|
|
|
setActiveNav();
|
|
initHistorySpy();
|
|
//#endregion
|