您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在右下角添加一个悬浮按钮,点击后弹出设置页数范围的对话框。UI现代简洁,兼容性强,支持桌面和手机。
// ==UserScript== // @name Hitomi - 页数过滤器 // @namespace http://tampermonkey.net/ // @version 4.0 // @description 在右下角添加一个悬浮按钮,点击后弹出设置页数范围的对话框。UI现代简洁,兼容性强,支持桌面和手机。 // @author You (-Refined by AI) // @match https://hitomi.la/* // @grant none // @run-at document-idle // ==/UserScript== (function () { 'use strict'; // --- 配置区 --- const DEFAULT_MIN_PAGES = 1; const DEFAULT_MAX_PAGES = 800; // --- 配置区结束 --- let currentMinPages = DEFAULT_MIN_PAGES; let currentMaxPages = DEFAULT_MAX_PAGES; /** * 创建并注入全新的UI(一个悬浮按钮和一个<dialog>对话框) */ function createFilterUI() { // 1. 创建悬浮触发按钮 const triggerButton = document.createElement('button'); triggerButton.id = 'hf-open-dialog-btn'; triggerButton.innerHTML = ` <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"></polygon> </svg> <span>过滤</span>`; document.body.appendChild(triggerButton); // 2. 创建 <dialog> 对话框 const dialog = document.createElement('dialog'); dialog.id = 'hf-filter-dialog'; dialog.innerHTML = ` <div class="hf-dialog-content"> <h3>页数范围过滤</h3> <p>请输入要显示的页数范围,留空表示不限制。</p> <div class="hf-input-group"> <input type="tel" pattern="[0-9]*" id="hf-min-pages" placeholder="最小页数"> <span>-</span> <input type="tel" pattern="[0-9]*" id="hf-max-pages" placeholder="最大页数"> </div> <div class="hf-button-group"> <button id="hf-apply-btn" class="hf-btn-primary">应用</button> <button id="hf-close-btn" class="hf-btn-secondary">关闭</button> </div> <div id="hf-status"></div> </div> `; document.body.appendChild(dialog); // 3. 注入 CSS 样式 const style = document.createElement('style'); style.innerHTML = ` /* --- 悬浮按钮样式 --- */ #hf-open-dialog-btn { position: fixed; bottom: 20px; right: 20px; z-index: 99998; width: 60px; height: 60px; border-radius: 50%; background-color: #3a87ad; color: white; border: none; box-shadow: 0 4px 12px rgba(0,0,0,0.3); cursor: pointer; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; transition: transform 0.2s, background-color 0.2s; } #hf-open-dialog-btn:hover { background-color: #2c6a8a; transform: scale(1.05); } #hf-open-dialog-btn span { font-size: 12px; } /* --- Dialog 对话框样式 --- */ #hf-filter-dialog { width: 90vw; max-width: 320px; border: 1px solid #ddd; border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.2); padding: 0; /* 我们在内部容器控制padding */ margin: auto; } #hf-filter-dialog::backdrop { /* 对话框蒙层样式 */ background-color: rgba(0, 0, 0, 0.5); backdrop-filter: blur(2px); } #hf-filter-dialog .hf-dialog-content { padding: 20px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; } #hf-filter-dialog h3 { margin: 0 0 8px; font-size: 1.2em; text-align: center; color: #333; } #hf-filter-dialog p { margin: 0 0 16px; font-size: 0.9em; color: #666; text-align: center; } #hf-filter-dialog .hf-input-group { display: flex; align-items: center; gap: 10px; margin-bottom: 20px; } #hf-filter-dialog .hf-input-group input { width: 100%; padding: 10px; border: 1px solid #ccc; border-radius: 8px; font-size: 1em; text-align: center; -moz-appearance: textfield; } #hf-filter-dialog .hf-input-group input::-webkit-outer-spin-button, #hf-filter-dialog .hf-input-group input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } #hf-filter-dialog .hf-button-group { display: flex; gap: 10px; } #hf-filter-dialog button { width: 100%; padding: 12px; border-radius: 8px; border: none; font-size: 1em; font-weight: bold; cursor: pointer; transition: opacity 0.2s; } #hf-filter-dialog button:hover { opacity: 0.85; } #hf-filter-dialog .hf-btn-primary { background-color: #3a87ad; color: white; } #hf-filter-dialog .hf-btn-secondary { background-color: #e0e0e0; color: #333; } #hf-status { margin-top: 15px; text-align: center; font-size: 0.8em; color: #777; min-height: 1.2em; } `; document.head.appendChild(style); // 4. 绑定事件 triggerButton.addEventListener('click', () => dialog.showModal()); document.getElementById('hf-close-btn').addEventListener('click', () => dialog.close()); document.getElementById('hf-apply-btn').addEventListener('click', () => { const minInput = document.getElementById('hf-min-pages'); const maxInput = document.getElementById('hf-max-pages'); minInput.value = minInput.value.replace(/[^0-9]/g, ''); maxInput.value = maxInput.value.replace(/[^0-9]/g, ''); let newMin = parseInt(minInput.value, 10) || 1; let newMax = parseInt(maxInput.value, 10) || Infinity; if (newMin > newMax) { [newMin, newMax] = [newMax, newMin]; minInput.value = newMin; maxInput.value = (newMax === Infinity) ? '' : newMax; } saveSettings(newMin, newMax); applyFilterNow(); dialog.close(); // 应用后自动关闭 }); } // ... (saveSettings, loadSettings, applyFilterNow, main 函数与之前版本相同) ... // ... 将版本 3.5 的 saveSettings, loadSettings, applyFilterNow, main 函数完整复制到这里 ... function saveSettings(min, max) { currentMinPages = min; currentMaxPages = max; localStorage.setItem('hf_min_pages', min); localStorage.setItem('hf_max_pages', max === Infinity ? '' : max); } function loadSettings() { const savedMin = localStorage.getItem('hf_min_pages'); const savedMax = localStorage.getItem('hf_max_pages'); currentMinPages = parseInt(savedMin, 10) || DEFAULT_MIN_PAGES; currentMaxPages = parseInt(savedMax, 10) || Infinity; const minInput = document.getElementById('hf-min-pages'); if (minInput) minInput.value = currentMinPages > 1 ? currentMinPages : ''; const maxInput = document.getElementById('hf-max-pages'); if (maxInput) maxInput.value = (currentMaxPages === Infinity) ? '' : currentMaxPages; } function applyFilterNow() { const statusElement = document.getElementById('hf-status'); const galleryItems = document.querySelectorAll('.gallery-content > div[class]'); if (galleryItems.length === 0) { if (statusElement) statusElement.textContent = '未找到作品'; return; } let visibleCount = 0; let totalCount = 0; galleryItems.forEach(item => { const h1 = item.querySelector('h1.lillie'); if (!h1) return; totalCount++; item.style.display = ''; let pages = -1; const firstNode = h1.childNodes[0]; if (firstNode && firstNode.nodeType === Node.TEXT_NODE) { const match = firstNode.textContent.trim().match(/^\((\d+)\)$/); if (match) pages = parseInt(match[1], 10); } if (pages === -1) { const pageInfoTd = Array.from(item.querySelectorAll('td')).find(td => /pages/i.test(td.textContent)); if(pageInfoTd) { const match = pageInfoTd.textContent.match(/(\d+)\s*pages/i); if (match) pages = parseInt(match[1], 10); } } if (pages !== -1) { if (pages < currentMinPages || pages > currentMaxPages) { item.style.display = 'none'; } else { visibleCount++; } } else { visibleCount++; } }); if (statusElement) statusElement.textContent = `显示 ${visibleCount} / ${totalCount} 项`; } function main() { createFilterUI(); // 注意,这里调用的是新函数 loadSettings(); let initialFilterDone = false; const tryInitialFilter = (retries = 0) => { if (document.querySelector('.gallery-content')) { applyFilterNow(); initialFilterDone = true; } else if (retries < 20) { setTimeout(() => tryInitialFilter(retries + 1), 500); } }; tryInitialFilter(); const observer = new MutationObserver((mutations) => { if (!initialFilterDone) return; const hasNewNodes = mutations.some(mutation => mutation.addedNodes.length > 0); if (hasNewNodes) { clearTimeout(observer.timer); observer.timer = setTimeout(applyFilterNow, 300); } }); const contentParent = document.querySelector('.container') || document.body; observer.observe(contentParent, { childList: true, subtree: true }); } if (document.body) { main(); } else { window.addEventListener('DOMContentLoaded', main, { once: true }); } })();