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 clamp(n, min, max) { return Math.min(max, Math.max(min, n)); } 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; let rafId = 0; const isMobile = () => window.matchMedia("(max-width: 992px)").matches; const syncRailHeight = () => { if (!yearsWrap || !rail || isMobile()) return; rail.style.height = `${yearsWrap.offsetHeight}px`; }; const setProgress = (progress) => { if (!indicator) return; indicator.style.transform = `scaleY(${clamp(progress, 0.04, 1)})`; }; const getActiveIndex = () => { const offset = getHeaderOffset() + 8; let idx = 0; for (let i = 0; i < blocks.length; i++) { if (blocks[i].getBoundingClientRect().top <= offset) idx = i; } return idx; }; /** * 进度只跟内容滚动的年份序号插值,不依赖左侧字号布局。 * 跨年时 (idx+local)/(n-1) 单调连续,避免「先变短再变长」。 */ const updateProgressFromScroll = () => { if (!indicator || !rail || isMobile()) return; syncRailHeight(); const n = navItems.length; if (n <= 1) { setProgress(1); return; } const offset = getHeaderOffset() + 8; const idx = getActiveIndex(); let local = 1; if (blocks[idx + 1]) { const curTop = blocks[idx].getBoundingClientRect().top; const nextTop = blocks[idx + 1].getBoundingClientRect().top; const span = nextTop - curTop; local = span > 0 ? clamp((offset - curTop) / span, 0, 1) : 1; } setProgress((idx + local) / (n - 1)); }; const setActive = (year, { scrollNav = true } = {}) => { if (!year || year === activeYear) return; activeYear = year; navItems.forEach((el) => { el.classList.toggle("is-active", el.dataset.year === year); }); if (scrollNav && isMobile()) { const current = navItems.find((el) => el.dataset.year === year); current?.scrollIntoView({ inline: "center", block: "nearest", behavior: "smooth" }); } }; const onScroll = () => { if (rafId) return; rafId = requestAnimationFrame(() => { rafId = 0; // 点击滚动过程中仍跟手更新进度;仅锁定高亮年份,避免中途抢焦点 if (!clicking) { const idx = getActiveIndex(); const year = navItems[idx]?.dataset.year; if (year) setActive(year); } updateProgressFromScroll(); }); }; 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; updateProgressFromScroll(); }; 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); }); }); if (window.lenis) window.lenis.on("scroll", onScroll); else window.addEventListener("scroll", onScroll, { passive: true }); if (typeof ResizeObserver !== "undefined" && yearsWrap) { new ResizeObserver(() => { syncRailHeight(); updateProgressFromScroll(); }).observe(yearsWrap); } window.addEventListener("resize", () => { syncRailHeight(); updateProgressFromScroll(); }); navItems[0]?.classList.add("is-active"); syncRailHeight(); updateProgressFromScroll(); const hashYear = (location.hash || "").replace(/^#year-/, ""); if (hashYear && blocks.some((el) => el.dataset.year === hashYear)) { requestAnimationFrame(() => scrollToYear(hashYear)); } } setActiveNav(); initHistorySpy(); //#endregion