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)}
`;
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 = `
`;
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();