// ==UserScript== // @name 图片查看器(一键打开网页全部图片,全屏单图查看和缩略图模式) // @namespace https://greasyfork.org/zh-CN/scripts/561244-%E5%9B%BE%E7%89%87%E6%9F%A5%E7%9C%8B%E5%99%A8-%E4%B8%80%E9%94%AE%E6%89%93%E5%BC%80%E7%BD%91%E9%A1%B5%E5%85%A8%E9%83%A8%E5%9B%BE%E7%89%87-%E5%85%A8%E5%B1%8F%E5%8D%95%E5%9B%BE%E6%9F%A5%E7%9C%8B%E5%92%8C%E7%BC%A9%E7%95%A5%E5%9B%BE%E6%A8%A1%E5%BC%8F // @version 1.5 // @description 自动提取网页所有图片,支持全屏沉浸式浏览、多宫格缩略图预览,退出时精准定位该图片位置。 // @author shao51920 // @match *://*/* // @grant none // @license MIT // @downloadURL https://update.greasyfork.icu/scripts/561244/%E5%9B%BE%E7%89%87%E6%9F%A5%E7%9C%8B%E5%99%A8%EF%BC%88%E4%B8%80%E9%94%AE%E6%89%93%E5%BC%80%E7%BD%91%E9%A1%B5%E5%85%A8%E9%83%A8%E5%9B%BE%E7%89%87%EF%BC%8C%E5%85%A8%E5%B1%8F%E5%8D%95%E5%9B%BE%E6%9F%A5%E7%9C%8B%E5%92%8C%E7%BC%A9%E7%95%A5%E5%9B%BE%E6%A8%A1%E5%BC%8F%EF%BC%89.user.js // @updateURL https://update.greasyfork.icu/scripts/561244/%E5%9B%BE%E7%89%87%E6%9F%A5%E7%9C%8B%E5%99%A8%EF%BC%88%E4%B8%80%E9%94%AE%E6%89%93%E5%BC%80%E7%BD%91%E9%A1%B5%E5%85%A8%E9%83%A8%E5%9B%BE%E7%89%87%EF%BC%8C%E5%85%A8%E5%B1%8F%E5%8D%95%E5%9B%BE%E6%9F%A5%E7%9C%8B%E5%92%8C%E7%BC%A9%E7%95%A5%E5%9B%BE%E6%A8%A1%E5%BC%8F%EF%BC%89.meta.js // ==/UserScript== (function() { 'use strict'; let images = []; let currentIndex = 0; let scale = 1; let rotation = 0; let playTimer = null; const MIN_SIZE = 200; // --- 1. 定位修复:针对小红书 SPA 框架优化 --- async function scrollToOriginal(idx) { if (!images[idx]) return; // 彻底关闭查看器 UI viewer.style.display = 'none'; if (playTimer) { clearInterval(playTimer); playTimer = null; } if (document.fullscreenElement) { await document.exitFullscreen().catch(()=>{}); } // 尝试获取最新的 DOM 引用(防止节点被小红书回收) let targetEl = images[idx].el; // 如果原节点失效,尝试通过 URL 重新在页面中寻找 if (!document.body.contains(targetEl)) { const allImgs = document.querySelectorAll('img'); for (let img of allImgs) { if (img.src === images[idx].originalSrc) { targetEl = img; break; } } } if (!targetEl) return; setTimeout(() => { // 使用更兼容的 scrollIntoView 解决小红书内部容器滚动问题 targetEl.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' }); // --- 蓝色艺术光圈(强制 CSS 权重) --- const highlightColor = '#0078ff'; // 蓝色 targetEl.style.setProperty('outline', `8px solid ${highlightColor}`, 'important'); targetEl.style.setProperty('outline-offset', '10px', 'important'); targetEl.style.setProperty('box-shadow', `0 0 40px ${highlightColor}`, 'important'); targetEl.style.setProperty('transition', 'all 0.5s ease', 'important'); setTimeout(() => { targetEl.style.outline = 'none'; targetEl.style.boxShadow = 'none'; }, 2000); }, 250); // 增加缓冲时间确保全屏已完全退出 } // --- 2. 高清探测(含小红书参数剥离) --- function getHDUrl(img) { let src = img.currentSrc || img.src || img.getAttribute('data-src') || ""; if (!src || src.startsWith('data:')) return null; const host = window.location.hostname; // 小红书原图剥离逻辑 if (host.includes('xiaohongshu.com') || host.includes('xhscdn.com')) { return src.split('?')[0].split('@')[0].replace(/\/format,\w+/, ''); } // Wallhaven/Emonl 等站点逻辑保持不变 if (host.includes('wallhaven.cc')) { if (src.includes('/small/') || src.includes('/thumb/')) { return src.replace('th.wallhaven.cc', 'w.wallhaven.cc').replace(/\/(small|thumb)\//, '/full/').replace(/\/([^\/]+)\.(jpg|png)$/, '/wallhaven-$1.$2'); } } if (host.includes('emonl.com') || host.includes('kkc3.com')) { return src.replace(/-\d+x\d+\.(jpg|jpeg|png|webp)$/i, '.$1'); } const pA = img.closest('a'); if (pA && pA.href && pA.href.match(/\.(jpg|jpeg|png|webp|gif|bmp)(\?.*)?$/i)) return pA.href; return src; } // --- 3. UI 构造 --- const viewer = document.createElement('div'); viewer.id = 'gm-viewer-root'; Object.assign(viewer.style, { all: 'initial', display: 'none', position: 'fixed', top: 0, left: 0, width: '100vw', height: '100vh', backgroundColor: '#000', zIndex: '2147483647', overflow: 'hidden' }); const icons = { grid: ``, prev: ``, next: ``, rotate: ``, play: ``, pause: ``, dl: ``, close: `` }; viewer.innerHTML = `
${icons.grid}
${icons.prev}
${icons.next}
${icons.rotate}
${icons.play}
${icons.dl}
${icons.close}
`; document.body.appendChild(viewer); const mainImg = viewer.querySelector('#gm-active-img'); const gridView = viewer.querySelector('#gm-grid-view'); const info = viewer.querySelector('#gm-info'); function scan() { const tempImages = []; const urlSet = new Set(); document.querySelectorAll('img').forEach(img => { if (img.width >= MIN_SIZE || img.naturalWidth >= MIN_SIZE || !img.complete) { const url = getHDUrl(img); if (url && !urlSet.has(url)) { urlSet.add(url); tempImages.push({ url: url, el: img, originalSrc: img.src }); } } }); images = tempImages; return images.length > 0; } function update(idx) { if (images.length === 0) return; currentIndex = (idx + images.length) % images.length; scale = 1; rotation = 0; mainImg.style.transform = `scale(1) rotate(0deg)`; mainImg.src = images[currentIndex].url; info.innerText = `${currentIndex + 1} / ${images.length}`; } // --- 4. 交互绑定 --- window.addEventListener('keydown', e => { const k = e.key.toLowerCase(); if (e.altKey && k === 'v') { if (scan()) { viewer.style.display = 'block'; viewer.querySelector('#gm-img-wrap').style.display = 'flex'; gridView.style.display = 'none'; update(0); document.documentElement.requestFullscreen().catch(()=>{}); } } if (viewer.style.display === 'none') return; if (k === 'escape') { e.preventDefault(); scrollToOriginal(currentIndex); } if (e.key === 'ArrowRight') update(currentIndex + 1); if (e.key === 'ArrowLeft') update(currentIndex - 1); if (k === 'r') { rotation += 90; mainImg.style.transform = `scale(${scale}) rotate(${rotation}deg)`; } if (k === 'g') { const isG = gridView.style.display === 'grid'; gridView.style.display = isG ? 'none' : 'grid'; viewer.querySelector('#gm-img-wrap').style.display = isG ? 'flex' : 'none'; if (!isG) { gridView.innerHTML = images.map((imgObj, i) => `
` ).join(''); } } if (k === 'p') { if (playTimer) { clearInterval(playTimer); playTimer = null; viewer.querySelector('#btn-play').innerHTML = icons.play; } else { playTimer = setInterval(() => update(currentIndex + 1), 3000); viewer.querySelector('#btn-play').innerHTML = icons.pause; } } if (k === 'd') { e.preventDefault(); window.open(images[currentIndex].url, '_blank'); } }); mainImg.ondblclick = () => scrollToOriginal(currentIndex); gridView.addEventListener('dblclick', (e) => { if (e.target.tagName === 'IMG') { scrollToOriginal(parseInt(e.target.dataset.idx)); } }); viewer.querySelector('#btn-close').onclick = () => scrollToOriginal(currentIndex); viewer.querySelector('#btn-next').onclick = () => update(currentIndex + 1); viewer.querySelector('#btn-prev').onclick = () => update(currentIndex - 1); viewer.querySelector('#btn-rotate').onclick = () => { rotation += 90; mainImg.style.transform = `scale(${scale}) rotate(${rotation}deg)`; }; viewer.querySelector('#btn-dl').onclick = () => window.open(images[currentIndex].url, '_blank'); viewer.querySelector('#btn-grid').onclick = () => window.dispatchEvent(new KeyboardEvent('keydown', {key: 'g'})); viewer.querySelector('#btn-play').onclick = () => window.dispatchEvent(new KeyboardEvent('keydown', {key: 'p'})); gridView.onclick = e => { if (e.target.tagName === 'IMG') { gridView.style.display='none'; viewer.querySelector('#gm-img-wrap').style.display='flex'; update(parseInt(e.target.dataset.idx)); } }; viewer.addEventListener('wheel', e => { if (gridView.style.display === 'grid') return; e.preventDefault(); scale = Math.min(Math.max(0.3, scale + (e.deltaY > 0 ? -0.15 : 0.15)), 10); mainImg.style.transform = `scale(${scale}) rotate(${rotation}deg)`; }, { passive: false }); })();