From 80cd41fe03401249314fa99d479613065ff6f842 Mon Sep 17 00:00:00 2001 From: inliver233 Date: Tue, 24 Feb 2026 04:53:30 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=80=91=E5=B8=83=E6=B5=81?= =?UTF-8?q?=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/public/wtf_page.py | 87 ++++++++++++++++++++++++++---- 1 file changed, 78 insertions(+), 9 deletions(-) diff --git a/backend/app/api/public/wtf_page.py b/backend/app/api/public/wtf_page.py index 3d3a021..94abdc8 100644 --- a/backend/app/api/public/wtf_page.py +++ b/backend/app/api/public/wtf_page.py @@ -64,10 +64,13 @@ def _build_wtf_html(*, base_url: str) -> str: a:hover {{ text-decoration: underline; }} .wrap {{ - max-width: 980px; + max-width: 1180px; margin: 0 auto; padding: 22px 16px 44px; }} + @media (min-width: 1400px) {{ + .wrap {{ max-width: 1280px; }} + }} @media (max-width: 520px) {{ .wrap {{ padding: 18px 12px 40px; }} }} @@ -151,6 +154,25 @@ def _build_wtf_html(*, base_url: str) -> str: content-visibility: auto; contain-intrinsic-size: 800px; }} + @keyframes shimmer {{ + 0% {{ background-position: 0% 0; }} + 100% {{ background-position: 200% 0; }} + }} + .item.pending {{ + background: linear-gradient( + 90deg, + rgba(255, 250, 243, 0.86), + rgba(255, 250, 243, 0.56), + rgba(255, 250, 243, 0.86) + ); + background-size: 200% 100%; + animation: shimmer 1100ms ease-in-out infinite; + border-color: rgba(58, 38, 26, 0.10); + box-shadow: none; + }} + .item.pending img {{ + aspect-ratio: var(--ar, 16 / 9); + }} .item img {{ width: 100%; height: auto; @@ -165,9 +187,9 @@ def _build_wtf_html(*, base_url: str) -> str: }} .sentinel {{ text-align: center; - padding: 18px 0 0; + padding: 12px 0 0; color: var(--muted2); - font-size: 12px; + font-size: 11px; }} @@ -214,6 +236,7 @@ def _build_wtf_html(*, base_url: str) -> str: let inflight = 0; let rendered = 0; let target = 0; + let failStreak = 0; function isMobile() {{ return window.matchMedia && window.matchMedia("(max-width: 520px)").matches; @@ -226,16 +249,29 @@ def _build_wtf_html(*, base_url: str) -> str: const slow = effectiveType.includes("2g") || effectiveType.includes("3g"); const base = mobile - ? {{ initial: 8, step: 6, maxInflight: 8 }} - : {{ initial: 12, step: 10, maxInflight: 14 }}; + ? {{ initial: 12, step: 8, maxInflight: 12 }} + : {{ initial: 18, step: 14, maxInflight: 22 }}; return {{ initial: base.initial, step: base.step, - maxInflight: slow ? Math.max(4, Math.floor(base.maxInflight / 2)) : base.maxInflight, + maxInflight: slow ? Math.max(6, Math.floor(base.maxInflight * 0.6)) : base.maxInflight, }}; }} + function pickSkeletonRatio() {{ + const o = String(baseParams.get("orientation") || "").trim().toLowerCase(); + if (o === "square") return "1 / 1"; + if (o === "portrait") return "2 / 3"; + if (o === "landscape") return "16 / 9"; + + const adaptiveRaw = String(baseParams.get("adaptive") || "").trim().toLowerCase(); + const adaptiveOn = baseParams.has("adaptive") && adaptiveRaw !== "0" && adaptiveRaw !== "false"; + if (adaptiveOn) return isMobile() ? "2 / 3" : "16 / 9"; + + return isMobile() ? "3 / 4" : "16 / 9"; + }} + function buildUrl() {{ const p = new URLSearchParams(baseParams); p.set("t", String(Date.now()) + "_" + String(seq++)); @@ -264,10 +300,16 @@ def _build_wtf_html(*, base_url: str) -> str: inflight += 1; updateSentinel(); + const item = document.createElement("div"); + item.className = "item pending"; + item.style.setProperty("--ar", pickSkeletonRatio()); + const img = new Image(); img.decoding = "async"; img.referrerPolicy = "no-referrer"; img.alt = ""; + item.appendChild(img); + feed.appendChild(item); let tries = 0; const load = () => {{ @@ -278,7 +320,9 @@ def _build_wtf_html(*, base_url: str) -> str: img.onload = () => {{ inflight = Math.max(0, inflight - 1); rendered += 1; - appendItem(img); + failStreak = 0; + item.classList.remove("pending"); + requestAnimationFrame(() => item.classList.add("loaded")); updateSentinel(); ensure(); }}; @@ -288,7 +332,15 @@ def _build_wtf_html(*, base_url: str) -> str: return; }} inflight = Math.max(0, inflight - 1); + failStreak += 1; + try {{ item.remove(); }} catch (e) {{}} updateSentinel(); + if (failStreak >= 8) {{ + paused = true; + toggle.textContent = "继续加载"; + sentinel.textContent = "连续失败较多:可能网络不稳定或当前过滤无匹配,请放宽条件后重试。"; + return; + }} ensure(); }}; @@ -305,7 +357,12 @@ def _build_wtf_html(*, base_url: str) -> str: function bump() {{ const c = cfg(); - target = Math.max(target, rendered + inflight + c.step); + const dist = Math.max(0, document.documentElement.scrollHeight - (window.scrollY + window.innerHeight)); + let m = 1; + if (dist < window.innerHeight * 0.8) m = 4; + else if (dist < window.innerHeight * 1.6) m = 3; + else if (dist < window.innerHeight * 2.6) m = 2; + target = Math.max(target, rendered + inflight + c.step * m); ensure(); }} @@ -322,9 +379,21 @@ def _build_wtf_html(*, base_url: str) -> str: for (const e of entries) {{ if (e.isIntersecting) bump(); }} - }}, {{ root: null, rootMargin: "2200px 0px", threshold: 0 }}); + }}, {{ root: null, rootMargin: "5200px 0px", threshold: 0 }}); io.observe(sentinel); + let scrollTick = 0; + window.addEventListener("scroll", () => {{ + if (paused) return; + if (scrollTick) return; + scrollTick = 1; + requestAnimationFrame(() => {{ + scrollTick = 0; + const dist = Math.max(0, document.documentElement.scrollHeight - (window.scrollY + window.innerHeight)); + if (dist < window.innerHeight * 3.2) bump(); + }}); + }}, {{ passive: true }}); + // Initial fill. target = cfg().initial; ensure();