@@ -64,10 +64,13 @@ def u(path: str) -> str:
6464 a:hover {{ text-decoration: underline; }}
6565
6666 .wrap {{
67- max-width: 980px ;
67+ max-width: 1180px ;
6868 margin: 0 auto;
6969 padding: 22px 16px 44px;
7070 }}
71+ @media (min-width: 1400px) {{
72+ .wrap {{ max-width: 1280px; }}
73+ }}
7174 @media (max-width: 520px) {{
7275 .wrap {{ padding: 18px 12px 40px; }}
7376 }}
@@ -151,6 +154,25 @@ def u(path: str) -> str:
151154 content-visibility: auto;
152155 contain-intrinsic-size: 800px;
153156 }}
157+ @keyframes shimmer {{
158+ 0% {{ background-position: 0% 0; }}
159+ 100% {{ background-position: 200% 0; }}
160+ }}
161+ .item.pending {{
162+ background: linear-gradient(
163+ 90deg,
164+ rgba(255, 250, 243, 0.86),
165+ rgba(255, 250, 243, 0.56),
166+ rgba(255, 250, 243, 0.86)
167+ );
168+ background-size: 200% 100%;
169+ animation: shimmer 1100ms ease-in-out infinite;
170+ border-color: rgba(58, 38, 26, 0.10);
171+ box-shadow: none;
172+ }}
173+ .item.pending img {{
174+ aspect-ratio: var(--ar, 16 / 9);
175+ }}
154176 .item img {{
155177 width: 100%;
156178 height: auto;
@@ -165,9 +187,9 @@ def u(path: str) -> str:
165187 }}
166188 .sentinel {{
167189 text-align: center;
168- padding: 18px 0 0;
190+ padding: 12px 0 0;
169191 color: var(--muted2);
170- font-size: 12px ;
192+ font-size: 11px ;
171193 }}
172194 </style>
173195</head>
@@ -214,6 +236,7 @@ def u(path: str) -> str:
214236 let inflight = 0;
215237 let rendered = 0;
216238 let target = 0;
239+ let failStreak = 0;
217240
218241 function isMobile() {{
219242 return window.matchMedia && window.matchMedia("(max-width: 520px)").matches;
@@ -226,16 +249,29 @@ def u(path: str) -> str:
226249 const slow = effectiveType.includes("2g") || effectiveType.includes("3g");
227250
228251 const base = mobile
229- ? {{ initial: 8 , step: 6 , maxInflight: 8 }}
230- : {{ initial: 12 , step: 10 , maxInflight: 14 }};
252+ ? {{ initial: 12 , step: 8 , maxInflight: 12 }}
253+ : {{ initial: 18 , step: 14 , maxInflight: 22 }};
231254
232255 return {{
233256 initial: base.initial,
234257 step: base.step,
235- maxInflight: slow ? Math.max(4 , Math.floor(base.maxInflight / 2 )) : base.maxInflight,
258+ maxInflight: slow ? Math.max(6 , Math.floor(base.maxInflight * 0.6 )) : base.maxInflight,
236259 }};
237260 }}
238261
262+ function pickSkeletonRatio() {{
263+ const o = String(baseParams.get("orientation") || "").trim().toLowerCase();
264+ if (o === "square") return "1 / 1";
265+ if (o === "portrait") return "2 / 3";
266+ if (o === "landscape") return "16 / 9";
267+
268+ const adaptiveRaw = String(baseParams.get("adaptive") || "").trim().toLowerCase();
269+ const adaptiveOn = baseParams.has("adaptive") && adaptiveRaw !== "0" && adaptiveRaw !== "false";
270+ if (adaptiveOn) return isMobile() ? "2 / 3" : "16 / 9";
271+
272+ return isMobile() ? "3 / 4" : "16 / 9";
273+ }}
274+
239275 function buildUrl() {{
240276 const p = new URLSearchParams(baseParams);
241277 p.set("t", String(Date.now()) + "_" + String(seq++));
@@ -264,10 +300,16 @@ def u(path: str) -> str:
264300 inflight += 1;
265301 updateSentinel();
266302
303+ const item = document.createElement("div");
304+ item.className = "item pending";
305+ item.style.setProperty("--ar", pickSkeletonRatio());
306+
267307 const img = new Image();
268308 img.decoding = "async";
269309 img.referrerPolicy = "no-referrer";
270310 img.alt = "";
311+ item.appendChild(img);
312+ feed.appendChild(item);
271313
272314 let tries = 0;
273315 const load = () => {{
@@ -278,7 +320,9 @@ def u(path: str) -> str:
278320 img.onload = () => {{
279321 inflight = Math.max(0, inflight - 1);
280322 rendered += 1;
281- appendItem(img);
323+ failStreak = 0;
324+ item.classList.remove("pending");
325+ requestAnimationFrame(() => item.classList.add("loaded"));
282326 updateSentinel();
283327 ensure();
284328 }};
@@ -288,7 +332,15 @@ def u(path: str) -> str:
288332 return;
289333 }}
290334 inflight = Math.max(0, inflight - 1);
335+ failStreak += 1;
336+ try {{ item.remove(); }} catch (e) {{}}
291337 updateSentinel();
338+ if (failStreak >= 8) {{
339+ paused = true;
340+ toggle.textContent = "继续加载";
341+ sentinel.textContent = "连续失败较多:可能网络不稳定或当前过滤无匹配,请放宽条件后重试。";
342+ return;
343+ }}
292344 ensure();
293345 }};
294346
@@ -305,7 +357,12 @@ def u(path: str) -> str:
305357
306358 function bump() {{
307359 const c = cfg();
308- target = Math.max(target, rendered + inflight + c.step);
360+ const dist = Math.max(0, document.documentElement.scrollHeight - (window.scrollY + window.innerHeight));
361+ let m = 1;
362+ if (dist < window.innerHeight * 0.8) m = 4;
363+ else if (dist < window.innerHeight * 1.6) m = 3;
364+ else if (dist < window.innerHeight * 2.6) m = 2;
365+ target = Math.max(target, rendered + inflight + c.step * m);
309366 ensure();
310367 }}
311368
@@ -322,9 +379,21 @@ def u(path: str) -> str:
322379 for (const e of entries) {{
323380 if (e.isIntersecting) bump();
324381 }}
325- }}, {{ root: null, rootMargin: "2200px 0px", threshold: 0 }});
382+ }}, {{ root: null, rootMargin: "5200px 0px", threshold: 0 }});
326383 io.observe(sentinel);
327384
385+ let scrollTick = 0;
386+ window.addEventListener("scroll", () => {{
387+ if (paused) return;
388+ if (scrollTick) return;
389+ scrollTick = 1;
390+ requestAnimationFrame(() => {{
391+ scrollTick = 0;
392+ const dist = Math.max(0, document.documentElement.scrollHeight - (window.scrollY + window.innerHeight));
393+ if (dist < window.innerHeight * 3.2) bump();
394+ }});
395+ }}, {{ passive: true }});
396+
328397 // Initial fill.
329398 target = cfg().initial;
330399 ensure();
0 commit comments