let gallery; let sentinel; let counter; let select; let observer; let offset = 0; let loading = false; let hasMore = true; const urlParams = new URLSearchParams(window.location.search); let height = urlParams.get("height") || (window.innerWidth > 1600 ? 320 : 220); let sort = urlParams.get("sort") || "time"; let tags = urlParams.get("tags") || ""; const limit = window.innerWidth > 1600 ? 50 : 20; function resetGallery() { imgviewer.resetList(); gallery.replaceChildren(); height = urlParams.get("height") || (window.innerWidth > 1600 ? 320 : 220); offset = 0; hasMore = true; loadMore(); } async function loadMore() { if (loading || !hasMore) return; let url = `/api/files?offset=${offset}&limit=${limit}&sort=${sort}` observer.unobserve(sentinel); loading = true; if (tags) url += `&tags=${tags}` try { const res = await fetch(url); const data = await res.json(); switch (res.status) { case 200: break; case 401: alert("Session expired"); return; case 500: if (data.error) { hasMore = false; alert(`${data.msg}: ${data.error}`); return } } appendFiles(data.files); hasMore = data.offset + data.files.length < data.total; offset += data.files.length; counter.textContent = data.total; if (!sort) sort = data.sort; } catch (err) { console.error(err); hasMore = false; } finally { loading = false; // 加载完成下一帧重新观察,强制刷新交叉状态 requestAnimationFrame(() => { observer.observe(sentinel); }); } } function appendFiles(files) { imgviewer.appendFiles(files); const fragment = document.createDocumentFragment(); for (const file of files) { const jWidth = file.width * height / file.height; const jPadding = file.height / file.width * 100; const wrapper = document.createElement("div"); wrapper.style.width = jWidth + "px"; wrapper.style.flexGrow = jWidth; const i = document.createElement("i"); i.style.paddingBottom = jPadding + "%"; if (sort == "size" || sort == "size-down") { const span = document.createElement("span"); const p = document.createElement("p"); p.textContent = `${file.ext} | ${file.thumbed ? "☇ " : ""}${file.size_str}`; span.appendChild(p); i.appendChild(span); } wrapper.appendChild(i); const imgs = document.createElement("div"); imgs.id = "imgs"; const img = document.createElement("img"); img.loading = "lazy"; img.decoding = "async"; img.tabIndex = "0"; if (file.thumbed) { img.src = `/thumbnails/${file.id_str}.jpg`; img.dataset.highres = `/files/${file.id_str}.${file.ext}`; } else { img.src = `/files/${file.id_str}.${file.ext}`; } let artwork = file.edges.artwork; img.alt = `trpic_artwork_${artwork.id}_${file.id_str}`; img.dataset.id_str = file.id_str; img.dataset.artwork_id = artwork.id; imgs.appendChild(img); if (file.thumbhash) { const thumb = document.createElement("img"); thumb.id = "thumbhash"; thumb.src = thumbHashToDataURL(file.thumbhash); imgs.appendChild(thumb); img.onload = () => { img.onload = null; thumb.style.opacity = 0; setInterval(() => { thumb.remove(); }, 1000) }; } wrapper.appendChild(imgs); imgviewer.bindClickEvents(img); fragment.appendChild(wrapper); } gallery.appendChild(fragment); } document.addEventListener("DOMContentLoaded", () => { gallery = document.getElementById("gallery"); sentinel = document.getElementById("sentinel"); counter = document.getElementById("image-count"); select = document.getElementById('sort-select') select.value = sort select.addEventListener('change', () => { urlParams.set("sort", sort = select.value); history.replaceState(null, null, `/files?${urlParams.toString()}`); resetGallery() }) observer = new IntersectionObserver( (entries) => { if (entries[0].isIntersecting) { loadMore(); } }, { root: null, rootMargin: "800px", // 提前 800px 加载 threshold: 0, } ); observer.observe(sentinel); }); document.addEventListener("error", (e) => { let target = e.target; const tagName = target.tagName || ""; const curTimes = Number(target.dataset.retryTimes) || 0; if (tagName.toLowerCase() === "img") { if (curTimes >= 5) { target.alt = "loading failed"; } else { setTimeout(() => { target.dataset.retryTimes = curTimes + 1; target.src = target.src; }, 10000); } } else { target = null; } }, true);