Files
trpic/web/scripts/load_image/load_images.js

252 lines
7.0 KiB
JavaScript

let gallery;
let sentinel;
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") || "",
or_tags = urlParams.get("or_tags") || "",
not_tags = urlParams.get("not_tags") || "";
let socials = urlParams.get("socials") || "",
or_socials = urlParams.get("or_socials") || "",
not_socials = urlParams.get("not_socials") || "";
let platforms = urlParams.get("platforms") || "",
or_platforms = urlParams.get("or_platforms") || "",
not_platforms = urlParams.get("not_platforms") || "";
let formats = urlParams.get("formats") || "",
or_formats = urlParams.get("or_formats") || "",
not_formats = urlParams.get("not_formats") || "";
const limit = window.innerWidth > 1200 ? 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}`
if (or_tags) url += `&or_tags=${or_tags}`
if (not_tags) url += `&not_tags=${not_tags}`
if (socials) url += `&socials=${socials}`
if (or_socials) url += `&or_socials=${or_socials}`
if (not_socials) url += `&not_socials=${not_socials}`
if (platforms) url += `&platforms=${platforms}`
if (or_platforms) url += `&or_platforms=${or_platforms}`
if (not_platforms) url += `&not_platforms=${not_platforms}`
if (formats) url += `&formats=${formats}`
if (or_formats) url += `&or_formats=${or_formats}`
if (not_formats) url += `&not_formats=${not_formats}`
try {
const res = await fetch(url);
const data = await res.json();
switch (res.status) {
case 200:
break;
case 401:
alert(`Session expired: ${data.msg}: ${data.error}`);
return;
case 500:
if (data.error) {
hasMore = false;
alert(`${data.msg}: ${data.error}`);
return
}
}
appendFiles(data.files);
hasMore = sort != "random" && data.files.length == limit;
offset += data.files.length;
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.className = "info";
i.style.paddingBottom = jPadding + "%";
wrapper.appendChild(i);
const imgs = document.createElement("div");
imgs.className = "imgs";
const img = document.createElement("img");
if (gallery.childElementCount < 50) {
img.fetchPriority = "high";
} else {
img.loading = "lazy";
}
img.decoding = "async";
img.tabIndex = "0";
img.src = file.thumbed ? `/thumbnails/${file.id}.jpg` : `/files/${file.id}.${file.ext}`;
let artwork = file.edges.artwork;
img.id = img.alt = `trpic_artwork_${artwork.id}_${file.id}`;
img.dataset.id = file.id;
img.dataset.artwork_id = artwork.id;
imgs.appendChild(img);
if (file.thumbhash) {
const thumb = document.createElement("img");
thumb.alt = "thumbhash";
thumb.classList.add("thumbhash");
thumb.src = thumbHashToDataURL(file.thumbhash);
imgs.appendChild(thumb);
if (artwork.sensitive > 1) {
img.tabIndex = "-1";
const m = document.createElement("a");
m.tabIndex = "0";
m.textContent = "spoiler";
m.href = `javascript:`;
m.classList.add(artwork.sensitive > 2 ? "sensitive" : "nsfw");
imgs.appendChild(m);
m.addEventListener("click", (e) => {
thumb.style.opacity = m.style.opacity = 0;
m.style.pointerEvents = "none";
if (e.ctrlKey) {
thumb.remove();
m.remove();
img.focus({ preventScroll: true });
img.tabIndex = "0";
} else {
setTimeout(() => {
thumb.style.opacity = m.style.opacity = "";
m.style.pointerEvents = "";
m.tabIndex = "0";
img.blur();
img.tabIndex = "-1";
}, 15000)
m.tabIndex = "-1";
img.tabIndex = "0";
img.focus({ preventScroll: true });
}
})
} else {
img.onload = () => {
img.onload = null;
thumb.style.opacity = 0;
setTimeout(() => {
thumb.remove();
}, 1000)
};
}
}
wrapper.appendChild(imgs);
imgviewer.bindClickEvents(img);
fragment.appendChild(wrapper);
if (artwork.edges.socials) {
const tr = document.createElement("p");
tr.className = "top-right";
tr.textContent = artwork.edges.socials[0].display_name;
i.appendChild(tr);
}
const br = document.createElement("p");
br.className = "bottom-right";
switch (sort) {
case "size":
case "size-down":
br.textContent = `${file.ext} | ${file.size_str}`;
break;
case "resolution":
case "resolution-down":
br.textContent = `${file.width}x${file.height}px`;
break;
}
if (br.textContent) {
i.appendChild(br);
}
}
gallery.appendChild(fragment);
}
document.addEventListener("DOMContentLoaded", () => {
gallery = document.getElementById("gallery");
sentinel = document.getElementById("sentinel");
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);