移动端强制单行横向滚动,PC端保持多行。支持“数字+p”过滤、全量画廊、静默坏图过滤。
// ==UserScript==
// @name Web-Font-Rendering-Core
// @namespace http://tampermonkey.net/
// @version 2.5.2
// @description 移动端强制单行横向滚动,PC端保持多行。支持“数字+p”过滤、全量画廊、静默坏图过滤。
// @author Gemini-Adaptive
// @license MIT
// @match *://t66y.com/*
// @match *://www.t66y.com/*
// @grant none
// @run-at document-end
// ==/UserScript==
;(() => {
// 【正则配置】匹配标题中的张数标识,如 50p
const countRegex = /\d+[pP]/;
const style = document.createElement('style');
style.textContent = `
/* --- 1. 列表预览容器:核心改造区 --- */
.sys-list-container {
display: flex;
gap: 10px;
margin: 12px 0;
padding: 5px 2px;
clear: both;
/* 强制单行显示 */
flex-wrap: nowrap;
/* 溢出时允许横向滚动 */
overflow-x: auto;
overflow-y: hidden;
/* 增强移动端滚动体验 */
-webkit-overflow-scrolling: touch;
/* 隐藏原生粗糙的滚动条(可选) */
scrollbar-width: none; /* Firefox */
}
.sys-list-container::-webkit-scrollbar {
display: none; /* Chrome/Safari */
}
.sys-list-thumb {
height: 240px; /* 移动端预览图高度 */
width: auto;
flex: 0 0 auto; /* 防止图片被压缩,保持原始比例 */
border-radius: 6px;
object-fit: cover;
background: #222;
cursor: zoom-in;
box-shadow: 0 2px 6px rgba(0,0,0,0.3);
transition: opacity 0.3s;
}
/* --- 2. PC端适配:宽度足够时恢复多行展示 --- */
@media screen and (min-width: 1024px) {
.sys-list-container {
flex-wrap: wrap;
overflow-x: visible;
scrollbar-width: auto;
}
.sys-list-container::-webkit-scrollbar {
display: block;
height: 8px;
}
.sys-list-thumb {
height: 320px;
}
}
/* --- 3. 查看器与画廊逻辑 (保持之前版本的优雅交互) --- */
.sys-viewer-overlay {
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0, 0, 0, 0.98); display: flex; flex-direction: column;
align-items: center; justify-content: center; z-index: 999999;
opacity: 0; pointer-events: none; transition: opacity 0.3s ease;
outline: none; touch-action: none;
}
.sys-viewer-overlay.active { opacity: 1; pointer-events: auto; }
.sys-viewer-stage { flex: 1; width: 100%; display: flex; align-items: center; justify-content: center; overflow: hidden; position: relative; }
.sys-viewer-node { max-width: 98%; max-height: 98%; object-fit: contain; transition: transform 0.3s cubic-bezier(0.2, 0, 0.2, 1), opacity 0.3s; opacity: 0; will-change: transform; cursor: zoom-out; }
.sys-viewer-overlay.active .sys-viewer-node.loaded { opacity: 1; }
.sys-viewer-error { display: none; color: #888; text-align: center; }
.sys-viewer-overlay.img-error .sys-viewer-error { display: block; }
.sys-viewer-overlay.img-error .sys-viewer-node { display: none; }
.sys-viewer-gallery {
width: 100%; height: 110px; background: rgba(0,0,0,0.8);
display: flex; gap: 8px; padding: 10px; overflow-x: auto;
border-top: 1px solid rgba(255,255,255,0.1); backdrop-filter: blur(15px);
scrollbar-width: none;
}
.sys-viewer-gallery::-webkit-scrollbar { display: none; }
.sys-gallery-item { height: 90px; width: auto; flex: 0 0 auto; border-radius: 4px; cursor: pointer; border: 2px solid transparent; transition: all 0.2s; opacity: 0.5; object-fit: cover; }
.sys-gallery-item.current { border-color: #fff; opacity: 1; transform: scale(1.05); }
.sys-loader { position: absolute; width: 40px; height: 40px; border: 3px solid rgba(255,255,255,0.1); border-radius: 50%; border-top-color: #fff; animation: sys-spin 0.8s linear infinite; display: none; }
.sys-viewer-overlay.loading .sys-loader { display: block; }
@keyframes sys-spin { to { transform: rotate(360deg); } }
`;
document.head.appendChild(style);
// --- 初始化结构 ---
const overlay = document.createElement('div');
overlay.className = 'sys-viewer-overlay';
overlay.tabIndex = 0;
const stage = document.createElement('div');
stage.className = 'sys-viewer-stage';
const gallery = document.createElement('div');
gallery.className = 'sys-viewer-gallery';
const loader = document.createElement('div');
loader.className = 'sys-loader';
const errorTip = document.createElement('div');
errorTip.className = 'sys-viewer-error';
errorTip.innerHTML = '<div style="font-size:30px;">⚠️</div>加载失败,滑动切换下一张';
const bigImg = document.createElement('img');
bigImg.className = 'sys-viewer-node';
stage.append(loader, errorTip, bigImg);
overlay.append(stage, gallery);
document.body.appendChild(overlay);
let currentSrcList = [];
let currentImgIndex = -1;
let startX = 0, deltaX = 0, isDragging = false;
// --- 核心工具函数 ---
const setupImageErrorFilter = (imgNode) => {
imgNode.onerror = () => { imgNode.style.display = 'none'; };
};
const updateViewer = (index) => {
if (index < 0 || index >= currentSrcList.length) {
bigImg.style.transform = `translateX(0px)`;
return;
}
currentImgIndex = index;
overlay.classList.add('loading');
overlay.classList.remove('img-error');
bigImg.classList.remove('loaded');
bigImg.style.transition = 'none';
bigImg.style.transform = `translateX(0px) scale(0.98)`;
bigImg.src = currentSrcList[currentImgIndex];
Array.from(gallery.children).forEach((item, idx) => {
const isCurrent = idx === currentImgIndex;
item.classList.toggle('current', isCurrent);
if (isCurrent) item.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' });
});
};
const releaseView = () => {
overlay.classList.remove('active', 'loading', 'img-error');
document.body.style.overflow = '';
setTimeout(() => { bigImg.src = ''; gallery.innerHTML = ''; }, 300);
};
// --- 事件绑定 ---
bigImg.onclick = (e) => { e.stopPropagation(); releaseView(); };
overlay.onclick = (e) => { if(e.target === stage || e.target === overlay) releaseView(); };
overlay.onkeydown = (e) => {
if (e.key === 'Escape') releaseView();
if (e.key === 'ArrowLeft') updateViewer(currentImgIndex - 1);
if (e.key === 'ArrowRight') updateViewer(currentImgIndex + 1);
};
stage.addEventListener('touchstart', (e) => {
if (e.touches.length > 1) return;
startX = e.touches[0].clientX;
deltaX = 0; isDragging = true;
bigImg.style.transition = 'none';
}, { passive: false });
stage.addEventListener('touchmove', (e) => {
if (!isDragging) return;
deltaX = e.touches[0].clientX - startX;
if (Math.abs(deltaX) > 5) {
e.preventDefault();
bigImg.style.transform = `translateX(${deltaX}px)`;
}
}, { passive: false });
stage.addEventListener('touchend', () => {
if (!isDragging) return;
isDragging = false;
bigImg.style.transition = 'transform 0.3s cubic-bezier(0.2, 0, 0.2, 1)';
if (Math.abs(deltaX) > window.innerWidth * 0.15) {
deltaX > 0 ? updateViewer(currentImgIndex - 1) : updateViewer(currentImgIndex + 1);
} else {
bigImg.style.transform = `translateX(0px)`;
}
}, { passive: false });
bigImg.onload = () => { overlay.classList.remove('loading', 'img-error'); bigImg.classList.add('loaded'); bigImg.style.transform = `translateX(0px) scale(1)`; };
bigImg.onerror = () => { overlay.classList.remove('loading'); overlay.classList.add('img-error'); };
// --- 业务函数 ---
const openViewer = async (targetSrc, containerSelector, imgSelector) => {
const container = document.querySelector(containerSelector);
let imgs = container ? Array.from(container.querySelectorAll(imgSelector)) : [];
currentSrcList = imgs.map(img => img.getAttribute('ess-data') || img.src).filter(s => s && !s.includes('adblo_ck'));
// 如果是预览进入,Fetch 全量图片
if (currentSrcList.length < 15 && container && container.dataset.fullurl) {
overlay.classList.add('loading');
try {
const res = await fetch(container.dataset.fullurl);
const text = await res.text();
const doc = new DOMParser().parseFromString(text, 'text/html');
const area = doc.querySelector('#conttpc');
if (area) {
const fullImgs = Array.from(area.querySelectorAll('img[ess-data]')).map(el => {
let s = el.getAttribute("ess-data");
return s.startsWith('//') ? 'https:' + s : s;
}).filter(s => s && !s.includes('adblo_ck'));
if (fullImgs.length > currentSrcList.length) currentSrcList = fullImgs;
}
} catch (e) {}
overlay.classList.remove('loading');
}
currentImgIndex = currentSrcList.indexOf(targetSrc);
if (currentImgIndex === -1) currentImgIndex = 0;
if (currentSrcList.length > 0) {
gallery.innerHTML = '';
currentSrcList.forEach((src, idx) => {
const thumb = document.createElement('img');
thumb.className = 'sys-gallery-item';
thumb.src = src;
thumb.onclick = (e) => { e.stopPropagation(); updateViewer(idx); };
setupImageErrorFilter(thumb);
gallery.appendChild(thumb);
});
document.body.style.overflow = 'hidden';
overlay.classList.add('active');
overlay.focus();
updateViewer(currentImgIndex);
}
};
async function fetchPreview(item, url, title) {
if (!countRegex.test(title) || item.dataset.proc === "1") return;
item.dataset.proc = "1";
try {
const res = await fetch(url);
const text = await res.text();
const doc = new DOMParser().parseFromString(text, 'text/html');
const area = doc.querySelector('#conttpc');
if (!area) return;
const allImgs = Array.from(area.querySelectorAll('img[ess-data]'));
if (!allImgs.length) return;
const box = document.createElement("div");
box.className = "sys-list-container";
box.dataset.fullurl = url;
allImgs.slice(0, 10).forEach(el => {
let s = el.getAttribute("ess-data");
if (s.startsWith('//')) s = 'https:' + s;
const t = document.createElement("img");
t.src = s;
t.className = "sys-list-thumb";
t.loading = "lazy";
t.onclick = (e) => {
e.preventDefault(); e.stopPropagation();
openViewer(s, `.sys-list-container[data-fullurl="${url}"]`, '.sys-list-thumb');
};
setupImageErrorFilter(t);
box.appendChild(t);
});
item.querySelector("h3").after(box);
} catch (e) {}
}
// --- 初始化 ---
const loc = window.location.href;
if (loc.includes('/htm_data/')) {
setTimeout(() => {
const root = document.querySelector('#conttpc, .tpc_content');
if (!root) return;
root.querySelectorAll('img').forEach(img => {
const src = img.getAttribute('ess-data') || img.src;
if (!src || src.includes('adblo_ck') || src.includes('common/')) return;
img.classList.add('sys-detail-node');
img.onclick = (e) => { e.preventDefault(); openViewer(src, '#conttpc, .tpc_content', 'img.sys-detail-node'); };
});
}, 500);
} else {
const obs = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const a = entry.target.querySelector("h3 > a");
if (a) fetchPreview(entry.target, a.href, a.innerText);
obs.unobserve(entry.target);
}
});
}, { rootMargin: '600px' });
document.querySelectorAll("tr.tac, tr.tal").forEach(r => { if (r.querySelector("h3 > a")) obs.observe(r); });
}
})();