一键获取页面所有SVG图片,支持批量下载和复制SVG代码
当前为
// ==UserScript==
// @name 模版之家icon嗅探
// @namespace http://tampermonkey.net/
// @version 1.0
// @description 一键获取页面所有SVG图片,支持批量下载和复制SVG代码
// @author You
// @match https://www.bootstrapmb.com/*
// @grant GM_addStyle
// @grant GM_setClipboard
// @grant GM_notification
// @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js
// ==/UserScript==
(function() {
'use strict';
// 添加样式
GM_addStyle(`
#svgSnifferBtn {
position: fixed;
top: 20px;
right: 20px;
z-index: 9999;
background: #4CAF50;
color: white;
border: none;
padding: 10px 15px;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
}
#svgSnifferBtn:hover {
background: #45a049;
}
#svgModal {
display: none;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 80%;
max-width: 800px;
max-height: 80vh;
background: white;
z-index: 10000;
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
overflow: hidden;
font-family: Arial, sans-serif;
}
.modal-header {
background: #3498db;
color: white;
padding: 15px;
display: flex;
justify-content: space-between;
align-items: center;
}
.modal-header h2 {
margin: 0;
font-size: 1.2rem;
}
.close-btn {
background: none;
border: none;
color: white;
font-size: 1.5rem;
cursor: pointer;
}
.modal-content {
padding: 20px;
overflow-y: auto;
max-height: 60vh;
}
.svg-item {
display: flex;
align-items: center;
padding: 10px;
border-bottom: 1px solid #eee;
}
.svg-checkbox {
margin-right: 15px;
}
.svg-preview {
width: 40px;
height: 40px;
margin-right: 15px;
border: 1px solid #ddd;
display: flex;
align-items: center;
justify-content: center;
}
.svg-preview svg {
max-width: 100%;
max-height: 100%;
}
.svg-name {
flex-grow: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
z-index: 9999;
display: none;
}
.action-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 15px;
background: #f9f9f9;
border-bottom: 1px solid #eee;
}
.select-all-control {
display: flex;
align-items: center;
}
.action-buttons {
display: flex;
gap: 10px;
}
.action-btn {
padding: 8px 15px;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
}
.download-btn {
background: #27ae60;
color: white;
}
.copy-btn {
background: #2980b9;
color: white;
}
.loading {
text-align: center;
padding: 20px;
font-size: 1.2rem;
color: #666;
}
.copy-notification {
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
background-color: #4CAF50;
color: white;
padding: 10px 20px;
border-radius: 4px;
z-index: 100000;
opacity: 0;
transition: opacity 0.5s;
pointer-events: none;
white-space: nowrap;
}
`);
// 创建嗅探按钮
const snifferBtn = document.createElement('button');
snifferBtn.id = 'svgSnifferBtn';
snifferBtn.textContent = '嗅探SVG';
document.body.appendChild(snifferBtn);
// 创建模态框
const modal = document.createElement('div');
modal.id = 'svgModal';
modal.innerHTML = `
<div class="modal-header">
<h2>SVG图片列表</h2>
<button class="close-btn">×</button>
</div>
<div class="action-bar">
<div class="select-all-control">
<label>
<input type="checkbox" id="selectAll">
全选
</label>
</div>
<div class="action-buttons">
<button class="action-btn download-btn">下载选中</button>
<button class="action-btn copy-btn">复制SVG</button>
</div>
</div>
<div class="modal-content" id="svgList">
<div class="loading">正在收集SVG资源...</div>
</div>
`;
document.body.appendChild(modal);
// 创建遮罩层
const overlay = document.createElement('div');
overlay.className = 'overlay';
document.body.appendChild(overlay);
// 创建复制通知元素
const copyNotification = document.createElement('div');
copyNotification.className = 'copy-notification';
copyNotification.textContent = '已复制到剪贴板!';
document.body.appendChild(copyNotification);
// 全局变量存储SVG项目
let globalSvgItems = [];
// 收集SVG函数
function collectSVGs() {
const svgItems = [];
// 1. 收集主展示区SVG
const mainSvgContainer = document.querySelector('.item-container.svg.icons .item-thumb');
if (mainSvgContainer) {
const svg = mainSvgContainer.querySelector('svg');
if (svg) {
const title = document.querySelector('h1')?.textContent || '主SVG';
const id = 'main-svg';
svgItems.push({
name: title,
svg: svg.outerHTML,
id: id
});
}
}
// 2. 收集网格区SVG
const gridItems = document.querySelectorAll('.icon-grid .gridItem');
gridItems.forEach(item => {
const svgContainer = item.querySelector('.svgUrl');
if (svgContainer) {
const svg = svgContainer.querySelector('svg');
if (svg) {
const title = item.querySelector('.gridItem__title')?.textContent || '未命名';
const id = item.id || `svg-${Math.random().toString(36).substr(2, 9)}`;
svgItems.push({
name: title,
svg: svg.outerHTML,
id: id
});
}
}
});
// 3. 收集其他区域的SVG
const allSvgs = document.querySelectorAll('svg');
allSvgs.forEach(svg => {
// 跳过已经收集的SVG
if (!svgItems.some(item => item.svg === svg.outerHTML)) {
// 尝试获取最近的标题
let title = '未命名SVG';
let parent = svg.parentElement;
while (parent) {
if (parent.querySelector('h1, h2, h3, h4, h5, h6')) {
title = parent.querySelector('h1, h2, h3, h4, h5, h6').textContent;
break;
}
parent = parent.parentElement;
}
const id = `svg-${Math.random().toString(36).substr(2, 9)}`;
svgItems.push({
name: title,
svg: svg.outerHTML,
id: id
});
}
});
return svgItems;
}
// 显示SVG列表
function showSVGList() {
const svgList = document.getElementById('svgList');
svgList.innerHTML = '<div class="loading">正在收集SVG资源...</div>';
modal.style.display = 'block';
overlay.style.display = 'block';
// 使用setTimeout让UI有机会更新
setTimeout(() => {
try {
const svgItems = collectSVGs();
globalSvgItems = svgItems; // 存储到全局变量
if (svgItems.length === 0) {
svgList.innerHTML = '<div class="loading">没有找到SVG资源</div>';
return;
}
svgList.innerHTML = '';
svgItems.forEach(item => {
const itemElement = document.createElement('div');
itemElement.className = 'svg-item';
itemElement.innerHTML = `
<input type="checkbox" class="svg-checkbox" data-id="${item.id}" checked>
<div class="svg-preview">${item.svg}</div>
<div class="svg-name" title="${item.name}">${item.name}</div>
`;
svgList.appendChild(itemElement);
});
} catch (error) {
console.error('SVG嗅探错误:', error);
svgList.innerHTML = `<div class="loading">错误: ${error.message}</div>`;
}
}, 100);
}
// 下载选中的SVG
function downloadSelected() {
const checkboxes = document.querySelectorAll('.svg-checkbox:checked');
if (checkboxes.length === 0) {
alert('请至少选择一个SVG!');
return;
}
const zip = new JSZip();
checkboxes.forEach(checkbox => {
const id = checkbox.dataset.id;
const item = globalSvgItems.find(i => i.id === id);
if (item) {
// 清理文件名
const cleanName = item.name.replace(/[^\w\u4e00-\u9fa5]/g, '_');
zip.file(`${cleanName}.svg`, item.svg);
}
});
zip.generateAsync({type: 'blob'}).then(content => {
saveAs(content, 'svg_collection.zip');
});
}
// 使用TextArea复制文本
function copyUsingTextArea(text) {
return new Promise((resolve) => {
const textArea = document.createElement('textarea');
textArea.value = text;
textArea.style.position = 'fixed';
textArea.style.top = '0';
textArea.style.left = '0';
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
const successful = document.execCommand('copy');
if (successful) {
resolve(true);
} else {
resolve(false);
}
} catch (err) {
resolve(false);
} finally {
document.body.removeChild(textArea);
}
});
}
// 复制选中的纯SVG代码 (仅SVG代码,不包含注释)
async function copySelected() {
const checkboxes = document.querySelectorAll('.svg-checkbox:checked');
if (checkboxes.length === 0) {
alert('请至少选择一个SVG!');
return;
}
// 构建纯SVG代码字符串
let combinedCode = '';
// 如果有多个选中项目,按行分隔
if (checkboxes.length > 1) {
checkboxes.forEach(checkbox => {
const id = checkbox.dataset.id;
const item = globalSvgItems.find(i => i.id === id);
if (item) {
combinedCode += `${item.svg}\n\n`;
}
});
}
// 如果只有一个选中项目,直接复制
else {
const id = checkboxes[0].dataset.id;
const item = globalSvgItems.find(i => i.id === id);
if (item) {
combinedCode = item.svg;
}
}
// 尝试三种复制方法
let copySucceeded = false;
// 方法1: 使用Tampermonkey API
try {
GM_setClipboard(combinedCode, 'text');
copySucceeded = true;
} catch (e) {
console.log('GM_setClipboard failed, trying other methods');
}
// 方法2: 使用navigator.clipboard API (仅限安全上下文)
if (!copySucceeded && navigator.clipboard) {
try {
await navigator.clipboard.writeText(combinedCode);
copySucceeded = true;
} catch (err) {
console.log('navigator.clipboard failed');
}
}
// 方法3: 使用textArea方法 (最可靠)
if (!copySucceeded) {
copySucceeded = await copyUsingTextArea(combinedCode);
}
// 显示通知
if (copySucceeded) {
copyNotification.textContent = `已复制 ${checkboxes.length} 个SVG代码`;
copyNotification.style.opacity = '1';
// 3秒后隐藏通知
setTimeout(() => {
copyNotification.style.opacity = '0';
}, 3000);
} else {
// 如果都失败,使用Tampermonkey通知API或alert
try {
GM_notification({
text: `复制SVG失败!内容已保存到控制台`,
title: 'SVG嗅探器错误',
silent: false
});
} catch (e) {
alert('复制失败!请检查控制台获取SVG代码');
}
// 记录代码到控制台便于用户手动复制
console.log('========== 复制的SVG代码 ==========');
console.log(combinedCode);
console.log('=================================');
}
}
// 事件监听
snifferBtn.addEventListener('click', showSVGList);
modal.querySelector('.close-btn').addEventListener('click', () => {
modal.style.display = 'none';
overlay.style.display = 'none';
});
overlay.addEventListener('click', () => {
modal.style.display = 'none';
overlay.style.display = 'none';
});
modal.querySelector('.download-btn').addEventListener('click', downloadSelected);
modal.querySelector('.copy-btn').addEventListener('click', copySelected);
document.getElementById('selectAll').addEventListener('change', (e) => {
const checkboxes = document.querySelectorAll('.svg-checkbox');
checkboxes.forEach(checkbox => {
checkbox.checked = e.target.checked;
});
});
})();