let allImages = []; let visibleImages = []; let currentIndex = 0; let imageObserver = null; const expandedYears = new Set(); const monthNames = { "01": "一月", "02": "二月", "03": "三月", "04": "四月", "05": "五月", "06": "六月", "07": "七月", "08": "八月", "09": "九月", 10: "十月", 11: "十一月", 12: "十二月", }; // 初始化懒加载观察器 function initLazyLoading() { if (imageObserver) return; imageObserver = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { const img = entry.target; const src = img.dataset.src; if (src) { img.src = src; img.classList.add("loaded"); imageObserver.unobserve(img); } } }); }, { rootMargin: "50px 0px", threshold: 0.1, }, ); } // 观察图片元素 function observeImage(img) { if (imageObserver) { imageObserver.observe(img); } } async function discoverImages() { const loading = document.getElementById("loading"); const progressText = document.getElementById("loadingProgress"); const content = document.getElementById("content"); try { progressText.textContent = "正在加载图片列表..."; const response = await fetch("/api/i/images.json"); if (!response.ok) throw new Error("Failed to load images.json"); allImages = await response.json(); // Sort by date descending allImages.sort((a, b) => b.date.localeCompare(a.date)); loading.classList.add("hidden"); content.classList.remove("hidden"); document.getElementById("totalCount").textContent = allImages.length.toLocaleString(); const _years = [...new Set(allImages.map((img) => img.year))]; document.getElementById("yearCount").textContent = _years.length; const months = [ ...new Set(allImages.map((img) => `${img.year}-${img.month}`)), ]; document.getElementById("monthCount").textContent = months.length; populateFilters(); // 初始化懒加载 initLazyLoading(); // 默认只展开最近的年份 const years = [...new Set(allImages.map((img) => img.year))] .sort() .reverse(); if (years.length > 0) { expandedYears.add(years[0]); // 展开最新年份 } renderGallery(); } catch (error) { console.error("Error loading gallery:", error); progressText.textContent = "加载失败,请检查网络或稍后重试"; progressText.style.color = "#ef4444"; } } function populateFilters() { const years = [...new Set(allImages.map((img) => img.year))].sort().reverse(); const yearSelect = document.getElementById("yearFilter"); years.forEach((year) => { const option = document.createElement("option"); option.value = year; option.textContent = `${year}年`; yearSelect.appendChild(option); }); updateMonthFilter(); document .getElementById("yearFilter") .addEventListener("change", updateMonthFilter); } function updateMonthFilter() { const year = document.getElementById("yearFilter").value; const monthSelect = document.getElementById("monthFilter"); monthSelect.innerHTML = ''; if (year) { const months = [ ...new Set( allImages.filter((img) => img.year === year).map((img) => img.month), ), ].sort(); months.forEach((month) => { const option = document.createElement("option"); option.value = month; option.textContent = `${Number.parseInt(month)}月`; monthSelect.appendChild(option); }); } filterGallery(); } function filterGallery() { const year = document.getElementById("yearFilter").value; const month = document.getElementById("monthFilter").value; const search = document.getElementById("searchInput").value.toLowerCase(); visibleImages = allImages.filter((img) => { if (year && img.year !== year) return false; if (month && img.month !== month) return false; if (search && !img.filename.toLowerCase().includes(search)) return false; return true; }); renderGallery(); } function renderGallery() { const timeline = document.getElementById("timeline"); timeline.innerHTML = ""; if (visibleImages.length === 0) { timeline.innerHTML = '
没有找到匹配的图片
'; return; } const byYear = {}; visibleImages.forEach((img) => { if (!byYear[img.year]) byYear[img.year] = {}; if (!byYear[img.year][img.month]) byYear[img.year][img.month] = []; byYear[img.year][img.month].push(img); }); Object.keys(byYear) .sort() .reverse() .forEach((year) => { const yearEl = document.createElement("div"); yearEl.className = "timeline-year"; yearEl.dataset.year = year; const yearCount = Object.values(byYear[year]).reduce( (sum, imgs) => sum + imgs.length, 0, ); const isExpanded = expandedYears.has(year); const toggleIcon = isExpanded ? "▼" : "▶"; yearEl.innerHTML = `
${year.slice(-2)}

${year}年

${yearCount} 张照片 ${toggleIcon}
`; const yearHeader = yearEl.querySelector(".timeline-year-header"); yearHeader?.addEventListener("click", () => toggleYear(year)); if (isExpanded) { Object.keys(byYear[year]) .sort() .reverse() .forEach((month) => { const monthImages = byYear[year][month]; const monthEl = document.createElement("div"); monthEl.className = "month-group"; monthEl.dataset.month = month; monthEl.innerHTML = `
📆 ${monthNames[month] || `${month}月`} (${monthImages.length} 张)
`; const masonry = monthEl.querySelector(".gallery-masonry"); monthImages.forEach((img) => { const card = document.createElement("div"); card.className = "image-card"; card.dataset.filename = img.filename; card.dataset.date = img.date; card.onclick = () => openLightbox(img); // 创建包装器 const wrapper = document.createElement("div"); wrapper.className = "wrapper"; // 创建占位符 const placeholder = document.createElement("div"); placeholder.className = "image-placeholder"; placeholder.innerHTML = "📷"; // 创建图片元素(懒加载) const imgElement = document.createElement("img"); imgElement.dataset.src = img.url; imgElement.alt = img.filename; imgElement.className = "lazy-image"; imgElement.onload = () => { placeholder.style.display = "none"; imgElement.classList.add("loaded"); }; imgElement.onerror = () => { placeholder.innerHTML = "❌"; }; observeImage(imgElement); // 创建覆盖层 const overlay = document.createElement("div"); overlay.className = "overlay"; overlay.innerHTML = '🔍'; // 组装包装器 wrapper.appendChild(placeholder); wrapper.appendChild(imgElement); wrapper.appendChild(overlay); // 创建信息区域 const info = document.createElement("div"); info.className = "info"; info.innerHTML = `
${img.filename}
${img.date}
`; // 组装卡片 card.appendChild(wrapper); card.appendChild(info); masonry.appendChild(card); }); yearEl.appendChild(monthEl); }); } timeline.appendChild(yearEl); }); } // 切换年份展开/折叠 function toggleYear(year) { if (expandedYears.has(year)) { expandedYears.delete(year); } else { expandedYears.add(year); } renderGallery(); } function openLightbox(img) { const lightbox = document.getElementById("lightbox"); document.getElementById("lightboxImage").src = img.url; document.getElementById("lightboxFilename").textContent = img.filename; document.getElementById("lightboxMeta").textContent = img.date; currentIndex = visibleImages.indexOf(img); lightbox.classList.add("active"); } function closeLightbox() { document.getElementById("lightbox").classList.remove("active"); } function navigateImage(direction) { currentIndex += direction; if (currentIndex < 0) currentIndex = visibleImages.length - 1; if (currentIndex >= visibleImages.length) currentIndex = 0; const img = visibleImages[currentIndex]; if (img) { openLightbox(img); } } document.addEventListener("keydown", (e) => { if (document.getElementById("lightbox").classList.contains("active")) { if (e.key === "Escape") closeLightbox(); if (e.key === "ArrowLeft") navigateImage(-1); if (e.key === "ArrowRight") navigateImage(1); } }); document.getElementById("lightbox").addEventListener("click", (e) => { if (e.target.id === "lightbox") closeLightbox(); }); // 兼容内联事件处理器(index.html 里的 onchange/onclick) window.filterGallery = filterGallery; window.closeLightbox = closeLightbox; window.navigateImage = navigateImage; window.toggleYear = toggleYear; discoverImages();