// ==UserScript== // @name 淘宝天猫图片快速复制 // @namespace http://tampermonkey.net/ // @version 1.0.0 // @description 支持主图、缩略图、详情页图片:单张复制、批量复制链接、新窗口打开。 // @author superMan // @match *://detail.tmall.com/item* // @match *://item.taobao.com/item* // @match *://chaoshi.detail.tmall.com/item* // @match *://traveldetail.fliggy.com/item* // @match *://detail.tmall.hk/hk/item* // @grant GM_setClipboard // @grant GM_openInTab // @license MIT // ==/UserScript== (function () { 'use strict'; // --- 选择器配置 --- const CONFIG = { mainWrap: '[class*="mainPicWrap--"]', mainImg: '[class*="mainPic--"]', thumbItem: '[class*="thumbnail--"]', thumbWrap: '[class*="thumbnailsWrap--"]', detailContainer: '[class*="imageTextInfo--"], #imageTextInfo-container', // 详情页容器 detailImgs: '.descV8-container img, .descV8-singleImage img, #imageTextInfo-content img' // 详情页图片 }; // --- 样式注入 --- const STYLES = ` /* 突破布局限制 */ ${CONFIG.thumbWrap}, [class*="thumbnails--"], [class*="innerWrap--"] { overflow: visible !important; mask: none !important; -webkit-mask: none !important; } /* 顶部提示框 */ .mcp-notice { position: fixed; top: 250px; left: 50%; transform: translateX(-50%); background: #27ae60; color: white; padding: 10px 30px; font-size: 15px; font-weight: bold; z-index: 2147483647; border-radius: 6px; box-shadow: 0 4px 15px rgba(0,0,0,0.2); display: none; text-align: center; pointer-events: none; } /* 通用按钮容器 */ .mcp-group { position: absolute; display: flex; flex-direction: column; gap: 4px; z-index: 2000; transition: opacity 0.2s ease; } /* 主图常驻 */ .mcp-main-group { right: 10px; top: 10px; opacity: 1; } /* 缩略图悬浮 */ .mcp-thumb-group { right: -90px; top: 0; opacity: 0; pointer-events: none; padding-left: 40px; margin-left: -40px; } ${CONFIG.thumbItem}:hover .mcp-thumb-group { opacity: 1; pointer-events: auto; } /* 详情页图片悬浮 */ .mcp-detail-item-wrap { position: relative; display: block; } .mcp-detail-group { right: 10px; top: 10px; opacity: 0; pointer-events: none; } .mcp-detail-item-wrap:hover .mcp-detail-group { opacity: 1; pointer-events: auto; } /* 详情页顶部批量按钮 */ .mcp-batch-bar { padding: 10px; text-align: right; background: #f4f4f4; border-radius: 8px; margin-bottom: 10px; } /* 按钮基础样式 */ .mcp-btn { background: rgba(0, 0, 0, 0.7); backdrop-filter: blur(4px); color: #fff; border: 1px solid rgba(255,255,255,0.2); padding: 6px 10px; font-size: 11px; cursor: pointer; border-radius: 4px; white-space: nowrap; text-align: center; box-shadow: 0 2px 8px rgba(0,0,0,0.3); transition: all 0.1s; width: 95px; } .mcp-btn:hover { background: #27ae60; transform: scale(1.05); } .mcp-btn-batch { width: auto; background: #27ae60; font-weight: bold; } `; // --- 工具类 --- const Utils = { injectStyle(css) { const style = document.createElement('style'); style.textContent = css; document.head.appendChild(style); }, // 清理URL:处理 data-src 并截断后缀 getRealUrl(imgEl) { if (!imgEl) return ''; // 优先取 data-src (淘宝详情页懒加载真实地址) let url = imgEl.getAttribute('data-src') || imgEl.src; if (!url || url.includes('s.gif')) return ''; // 过滤掉占位图 let fullUrl = url.startsWith('//') ? 'https:' + url : url; const match = fullUrl.match(/^(.*?\.(jpg|jpeg|png|webp|gif))/i); return match ? match[1] : fullUrl; } }; // --- 通知系统 --- const Notifier = { el: null, timer: null, init() { this.el = document.createElement('div'); this.el.className = 'mcp-notice'; document.body.appendChild(this.el); }, show(msg) { clearTimeout(this.timer); this.el.innerText = msg; this.el.style.display = 'block'; this.timer = setTimeout(() => { this.el.style.display = 'none'; }, 2000); } }; // --- 核心逻辑 --- const App = { async execute(action, url) { if (!url) return; switch (action) { case 'copyLink': GM_setClipboard(url); Notifier.show('复制图片链接'); break; case 'copyImg': this.copyImageBinary(url); break; case 'open': GM_openInTab(url, false); Notifier.show('新窗口打开'); break; } }, async copyImageBinary(url) { Notifier.show('正在提取图片...'); try { const resp = await fetch(url); const blob = await resp.blob(); const img = new Image(); img.crossOrigin = "Anonymous"; img.src = URL.createObjectURL(blob); img.onload = () => { const canvas = document.createElement('canvas'); canvas.width = img.width; canvas.height = img.height; canvas.getContext('2d').drawImage(img, 0, 0); canvas.toBlob(async (pngBlob) => { await navigator.clipboard.write([new ClipboardItem({ 'image/png': pngBlob })]); Notifier.show('复制图片png'); }, 'image/png'); }; } catch (e) { Notifier.show('复制失败,请使用复制链接'); } }, createGroup(getSrcFn, extraClass = '') { const group = document.createElement('div'); group.className = `mcp-group ${extraClass}`; const actions = [ { name: '复制图片链接', type: 'copyLink' }, { name: '复制图片png', type: 'copyImg' }, { name: '新窗口打开', type: 'open' } ]; actions.forEach(act => { const btn = document.createElement('button'); btn.className = 'mcp-btn'; btn.innerText = act.name; btn.onclick = (e) => { e.preventDefault(); e.stopPropagation(); this.execute(act.type, getSrcFn()); }; group.appendChild(btn); }); return group; }, // 注入逻辑 scanAndInject() { // 1. 主图 const main = document.querySelector(CONFIG.mainWrap); if (main && !main.querySelector('.mcp-main-group')) { main.appendChild(this.createGroup(() => Utils.getRealUrl(main.querySelector(CONFIG.mainImg)), 'mcp-main-group')); } // 2. 缩略图 document.querySelectorAll(CONFIG.thumbItem).forEach(item => { if (item.querySelector('.mcp-thumb-group')) return; const img = item.querySelector('img'); if (img) { item.style.position = 'relative'; item.appendChild(this.createGroup(() => Utils.getRealUrl(img), 'mcp-thumb-group')); } }); // 3. 详情页图片 const detailContainer = document.querySelector(CONFIG.detailContainer); if (detailContainer) { // A. 注入批量按钮 if (!detailContainer.querySelector('.mcp-batch-bar')) { const bar = document.createElement('div'); bar.className = 'mcp-batch-bar'; const btn = document.createElement('button'); btn.className = 'mcp-btn mcp-btn-batch'; btn.innerText = '复制本详情页所有图片链接'; btn.onclick = () => { const urls = Array.from(detailContainer.querySelectorAll('img')) .map(i => Utils.getRealUrl(i)) .filter(u => u !== ''); GM_setClipboard(urls.join('\n')); Notifier.show(`已复制 ${urls.length} 张图片链接`); }; bar.appendChild(btn); detailContainer.prepend(bar); } // B. 注入单张操作 document.querySelectorAll(CONFIG.detailImgs).forEach(img => { if (img.parentElement.classList.contains('mcp-detail-item-wrap')) return; // 过滤掉已经在包裹层里的或太小的图片 if (img.width < 100 && img.height < 100) return; const wrap = document.createElement('div'); wrap.className = 'mcp-detail-item-wrap'; img.before(wrap); wrap.appendChild(img); wrap.appendChild(this.createGroup(() => Utils.getRealUrl(img), 'mcp-detail-group')); }); } } }; // --- 初始化 --- const init = () => { Utils.injectStyle(STYLES); Notifier.init(); // 详情页图片通常是动态加载,需要更高频或持续的观察 const observer = new MutationObserver(() => App.scanAndInject()); observer.observe(document.body, { childList: true, subtree: true }); App.scanAndInject(); }; if (document.readyState === 'complete') init(); else window.addEventListener('load', init); })();