您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在 Fandom wiki 网站上,当鼠标悬停在图片上时显示一个下载按钮,点击即可下载原图。采用JS动态计算尺寸,彻底修复图标变形问题。
// ==UserScript== // @name Fandom 图片下载器(选悬浮标识) // @name:en Fandom Image Hover Downloader // @namespace https://github.com/your-username-here // @version 1.3 // @description 在 Fandom wiki 网站上,当鼠标悬停在图片上时显示一个下载按钮,点击即可下载原图。采用JS动态计算尺寸,彻底修复图标变形问题。 // @description:en Shows a download button on mouse hover over images on Fandom wiki sites. Uses JS dynamic sizing to definitively fix icon distortion. // @author Gemini & Camellia895 // @match *://*.fandom.com/* // @grant none // @run-at document-end // @license MIT // ==/UserScript== (function() { 'use strict'; // --- 全局常量 --- const DOWNLOAD_SVG_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="60%" height="60%"><path d="M12 15.5l-5-5h3V2h4v8.5h3l-5 5zM5 20h14v-2H5v2z"></path></svg>`; // --- 工具函数: 节流 --- function throttle(func, limit) { let inThrottle; return function() { const args = arguments; const context = this; if (!inThrottle) { func.apply(context, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; } // --- 核心功能 --- function getOriginalImageUrl(url) { const match = url.match(/^(.*?\.(?:png|jpg|jpeg|gif|webp|bmp|svg))/i); return match ? match[1] : url.split('/revision/')[0] || url; } function getFilenameFromUrl(url) { try { return decodeURIComponent(url.split('/').pop()); } catch (e) { return url.split('/').pop(); } } async function downloadFile(url, filename) { try { const response = await fetch(url); if (!response.ok) throw new Error(`网络响应错误: ${response.statusText}`); const blob = await response.blob(); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(a.href); } catch (error) { console.error('下载失败:', error); alert(`下载失败: ${filename}\n原因: ${error.message}`); } } /** * 为单个图片元素添加悬浮下载功能。 * @param {HTMLImageElement} imgElement - 目标图片元素。 */ function addHoverDownloader(imgElement) { if (imgElement.dataset.hoverDownloaderAdded) return; imgElement.dataset.hoverDownloaderAdded = 'true'; const container = imgElement.closest('a') || imgElement.parentElement; if (!container) return; if (window.getComputedStyle(container).position === 'static') { container.style.position = 'relative'; } container.addEventListener('mouseenter', () => { if (container.querySelector('.hover-download-overlay-gemini')) return; const overlay = document.createElement('div'); overlay.className = 'hover-download-overlay-gemini'; overlay.style.cssText = ` position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.45); display: flex; align-items: center; justify-content: center; cursor: pointer; z-index: 9999; transition: opacity 0.2s ease; `; const iconContainer = document.createElement('div'); iconContainer.style.cssText = ` /* 外观和居中样式 */ background-color: rgba(0, 0, 0, 0.6); border-radius: 50%; border: 2px solid white; box-sizing: border-box; display: flex; align-items: center; justify-content: center; `; // [v1.3] 核心修复:使用 JavaScript 动态计算尺寸 // 1. 获取容器的实际渲染尺寸 const containerWidth = container.clientWidth; const containerHeight = container.clientHeight; // 2. 找到较短的一边 const shorterSide = Math.min(containerWidth, containerHeight); // 3. 将图标尺寸设置为较短边的 50%,确保为正方形且不变形 const iconSize = shorterSide * 0.5; iconContainer.style.width = `${iconSize}px`; iconContainer.style.height = `${iconSize}px`; // 确保图标在极小图片上不会过小 iconContainer.style.minWidth = '35px'; iconContainer.style.minHeight = '35px'; iconContainer.innerHTML = DOWNLOAD_SVG_ICON; overlay.appendChild(iconContainer); overlay.onclick = async (e) => { e.preventDefault(); e.stopPropagation(); const imageUrl = imgElement.src || imgElement.dataset.src; const originalUrl = getOriginalImageUrl(imageUrl); const filename = getFilenameFromUrl(originalUrl); iconContainer.innerHTML = '...'; await downloadFile(originalUrl, filename); if(overlay.parentElement) overlay.remove(); }; container.appendChild(overlay); }); container.addEventListener('mouseleave', () => { const activeOverlay = container.querySelector('.hover-download-overlay-gemini'); if (activeOverlay) activeOverlay.remove(); }); } /** * 扫描整个页面,为所有符合条件的图片添加下载功能。 */ function processPage() { const images = document.querySelectorAll('img[src*="static.wikia.nocookie.net/"]:not([data-hover-downloader-added])'); images.forEach(img => { if (img.offsetParent !== null && (img.clientWidth > 30 && img.clientHeight > 30)) { addHoverDownloader(img); } }); } // --- 主执行逻辑 --- const throttledProcessPage = throttle(processPage, 500); const observer = new MutationObserver(throttledProcessPage); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['src'] }); window.addEventListener('load', () => { throttledProcessPage(); setTimeout(throttledProcessPage, 1000); }); })();