您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
为图片批量添加自定义尺寸和颜色的背景,支持多种格式
// ==UserScript== // @name 图片批量修改自定义尺寸和颜色的背景(树洞先生) // @namespace http://tampermonkey.net/ // @version 2.1 // @description 为图片批量添加自定义尺寸和颜色的背景,支持多种格式 // @author 树洞先生 // @match *://*/* // @license MIT // @grant none // @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'; // 创建工具界面 function createToolInterface() { // 检查是否已存在工具 if (document.getElementById('whiteBackgroundTool')) { return; } // 创建主容器 const toolContainer = document.createElement('div'); toolContainer.id = 'whiteBackgroundTool'; toolContainer.innerHTML = ` <div style=" position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 400px; background: white; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.3); z-index: 10000; font-family: Arial, sans-serif; display: none; "> <div style=" background: #4CAF50; color: white; padding: 12px 16px; border-radius: 8px 8px 0 0; display: flex; justify-content: space-between; align-items: center; "> <div style="display: flex; align-items: center; gap: 8px;"> <span style="font-size: 16px;">✅</span> <span style="font-weight: bold;">批量添加白色背景</span> </div> <button id="closeTool" style=" background: none; border: none; color: white; font-size: 18px; cursor: pointer; padding: 0; width: 24px; height: 24px; ">×</button> </div> <div style="padding: 16px;"> <!-- 功能说明 --> <div style="margin-bottom: 16px;"> <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px;"> <span style="font-size: 14px;">📋</span> <span style="font-weight: bold; font-size: 14px;">功能说明:</span> </div> <ul style="margin: 0; padding-left: 20px; font-size: 13px; color: #666; line-height: 1.4;"> <li>创建自定义尺寸白色背景</li> <li>图片自动居中放置</li> <li>保持原图宽高比</li> <li>支持JPG、PNG、BMP等格式</li> </ul> </div> <!-- 自定义设置 --> <div style="margin-bottom: 16px;"> <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 10px;"> <span style="font-size: 14px;">⚙️</span> <span style="font-weight: bold; font-size: 14px;">自定义设置:</span> </div> <div style="background: #f8f9fa; padding: 12px; border-radius: 6px; border-left: 3px solid #4CAF50;"> <!-- 尺寸设置 --> <div style="display: flex; gap: 12px; margin-bottom: 10px;"> <div style="flex: 1;"> <label style="display: block; margin-bottom: 4px; font-size: 12px; color: #666;">宽度 (px)</label> <input type="number" id="canvasWidth" value="800" min="100" max="4000" style=" width: 100%; padding: 6px 8px; border: 1px solid #ddd; border-radius: 4px; font-size: 13px; box-sizing: border-box; "> </div> <div style="flex: 1;"> <label style="display: block; margin-bottom: 4px; font-size: 12px; color: #666;">高度 (px)</label> <input type="number" id="canvasHeight" value="800" min="100" max="4000" style=" width: 100%; padding: 6px 8px; border: 1px solid #ddd; border-radius: 4px; font-size: 13px; box-sizing: border-box; "> </div> </div> <!-- 颜色设置 --> <div> <label style="display: block; margin-bottom: 4px; font-size: 12px; color: #666;">背景颜色</label> <div style="display: flex; gap: 8px; align-items: center;"> <input type="color" id="backgroundColor" value="#ffffff" style=" width: 36px; height: 32px; border: 1px solid #ddd; border-radius: 4px; cursor: pointer; padding: 0; "> <input type="text" id="backgroundColorText" value="#ffffff" placeholder="#ffffff" style=" flex: 1; padding: 6px 8px; border: 1px solid #ddd; border-radius: 4px; font-size: 13px; font-family: monospace; box-sizing: border-box; "> </div> </div> </div> </div> <!-- 文件选择 --> <div style="margin-bottom: 16px;"> <label style=" display: block; margin-bottom: 8px; font-weight: bold; color: #333; font-size: 14px; ">选择图片文件:</label> <input type="file" id="imageInput" multiple accept="image/*" style=" width: 100%; padding: 8px; border: 2px dashed #ddd; border-radius: 5px; cursor: pointer; font-size: 13px; box-sizing: border-box; "> </div> <!-- 文件列表 --> <div id="fileList" style=" max-height: 120px; overflow-y: auto; margin-bottom: 16px; padding: 8px; background: #f9f9f9; border-radius: 5px; font-size: 12px; display: none; border: 1px solid #eee; "></div> <!-- 操作按钮 --> <div style="display: flex; gap: 10px;"> <button id="processImages" disabled style=" flex: 1; padding: 10px; background: #4CAF50; color: white; border: none; border-radius: 5px; cursor: pointer; font-weight: bold; font-size: 14px; ">🚀 开始处理</button> <button id="clearImages" disabled style=" padding: 10px 16px; background: #ff4444; color: white; border: none; border-radius: 5px; cursor: pointer; font-size: 14px; ">🗑️ 清空</button> </div> <!-- 进度条 --> <div id="progressContainer" style=" margin-top: 16px; display: none; "> <div style="margin-bottom: 8px;"> <span id="progressText" style="font-size: 13px; color: #666;">处理中...</span> </div> <div style=" width: 100%; height: 8px; background: #eee; border-radius: 4px; overflow: hidden; "> <div id="progressBar" style=" height: 100%; background: #4CAF50; width: 0%; transition: width 0.3s; "></div> </div> </div> </div> </div> `; document.body.appendChild(toolContainer); // 绑定事件 bindEvents(); } // 绑定事件处理 function bindEvents() { const toolPanel = document.querySelector('#whiteBackgroundTool > div'); const closeBtn = document.getElementById('closeTool'); const imageInput = document.getElementById('imageInput'); const processBtn = document.getElementById('processImages'); const clearBtn = document.getElementById('clearImages'); const fileList = document.getElementById('fileList'); // 背景设置相关元素 const canvasWidth = document.getElementById('canvasWidth'); const canvasHeight = document.getElementById('canvasHeight'); const backgroundColor = document.getElementById('backgroundColor'); const backgroundColorText = document.getElementById('backgroundColorText'); let selectedFiles = []; // 关闭工具 closeBtn.addEventListener('click', () => { toolPanel.style.display = 'none'; }); // 颜色选择器和文本框同步 backgroundColor.addEventListener('input', (e) => { backgroundColorText.value = e.target.value; }); backgroundColorText.addEventListener('input', (e) => { const color = e.target.value; if (/^#[0-9A-Fa-f]{6}$/.test(color)) { backgroundColor.value = color; } }); // 文件选择 imageInput.addEventListener('change', (e) => { selectedFiles = Array.from(e.target.files); updateFileList(selectedFiles); processBtn.disabled = selectedFiles.length === 0; clearBtn.disabled = selectedFiles.length === 0; }); // 清空文件 clearBtn.addEventListener('click', () => { selectedFiles = []; imageInput.value = ''; updateFileList(selectedFiles); processBtn.disabled = true; clearBtn.disabled = true; }); // 处理图片 processBtn.addEventListener('click', () => { if (selectedFiles.length > 0) { const settings = { width: parseInt(canvasWidth.value) || 800, height: parseInt(canvasHeight.value) || 800, backgroundColor: backgroundColor.value || '#ffffff' }; processImages(selectedFiles, settings); } }); // 更新文件列表显示 function updateFileList(files) { if (files.length === 0) { fileList.style.display = 'none'; return; } fileList.style.display = 'block'; fileList.innerHTML = ` <strong>已选择 ${files.length} 个文件:</strong><br> ${files.map((file, index) => `${index + 1}. ${file.name} (${(file.size / 1024 / 1024).toFixed(2)} MB)` ).join('<br>')} `; } } // 处理图片主函数 async function processImages(files, settings) { const progressContainer = document.getElementById('progressContainer'); const progressBar = document.getElementById('progressBar'); const progressText = document.getElementById('progressText'); const processBtn = document.getElementById('processImages'); // 显示进度条 progressContainer.style.display = 'block'; processBtn.disabled = true; const zip = new JSZip(); const processedImages = []; for (let i = 0; i < files.length; i++) { const file = files[i]; // 更新进度 const progress = ((i + 1) / files.length) * 100; progressBar.style.width = progress + '%'; progressText.textContent = `处理中... ${i + 1}/${files.length} - ${file.name}`; try { const processedImageBlob = await addWhiteBackground(file, settings); const fileName = getOutputFileName(file.name); zip.file(fileName, processedImageBlob); processedImages.push(fileName); } catch (error) { console.error(`处理文件 ${file.name} 时出错:`, error); } // 添加延迟,避免浏览器卡顿 await new Promise(resolve => setTimeout(resolve, 100)); } // 生成并下载ZIP文件 progressText.textContent = '生成下载文件...'; try { const zipBlob = await zip.generateAsync({type: 'blob'}); const timestamp = new Date().toISOString().slice(0, 19).replace(/[:-]/g, ''); const zipName = `processed_images_${settings.width}x${settings.height}_${timestamp}.zip`; saveAs(zipBlob, zipName); progressText.textContent = `✅ 完成!已处理 ${processedImages.length} 张图片`; setTimeout(() => { progressContainer.style.display = 'none'; processBtn.disabled = false; }, 3000); } catch (error) { console.error('生成ZIP文件出错:', error); progressText.textContent = '❌ 下载失败,请重试'; processBtn.disabled = false; } } // 为单张图片添加背景 function addWhiteBackground(file, settings) { return new Promise((resolve, reject) => { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); const img = new Image(); img.onload = function() { // 设置画布尺寸 canvas.width = settings.width; canvas.height = settings.height; // 填充背景颜色 ctx.fillStyle = settings.backgroundColor; ctx.fillRect(0, 0, settings.width, settings.height); // 计算图片缩放比例(保持宽高比) const imgWidth = img.naturalWidth; const imgHeight = img.naturalHeight; const scale = Math.min(settings.width / imgWidth, settings.height / imgHeight); // 计算绘制尺寸和位置 const drawWidth = imgWidth * scale; const drawHeight = imgHeight * scale; const x = (settings.width - drawWidth) / 2; const y = (settings.height - drawHeight) / 2; // 绘制图片(居中) ctx.drawImage(img, x, y, drawWidth, drawHeight); // 转换为Blob canvas.toBlob(resolve, 'image/jpeg', 0.95); }; img.onerror = () => reject(new Error('图片加载失败')); // 读取文件 const reader = new FileReader(); reader.onload = (e) => { img.src = e.target.result; }; reader.onerror = () => reject(new Error('文件读取失败')); reader.readAsDataURL(file); }); } // 生成输出文件名 function getOutputFileName(originalName) { const lastDotIndex = originalName.lastIndexOf('.'); if (lastDotIndex === -1) { return `${originalName}_with_bg.jpg`; } const name = originalName.substring(0, lastDotIndex); return `${name}_with_bg.jpg`; } // 创建启动按钮 function createLaunchButton() { const button = document.createElement('div'); button.innerHTML = '🖼️'; button.style.cssText = ` position: fixed; top: 20px; right: 20px; width: 50px; height: 50px; background: #4CAF50; color: white; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; font-size: 20px; z-index: 9999; box-shadow: 0 2px 10px rgba(0,0,0,0.3); transition: all 0.3s; `; button.addEventListener('mouseenter', () => { button.style.transform = 'scale(1.1)'; }); button.addEventListener('mouseleave', () => { button.style.transform = 'scale(1)'; }); button.addEventListener('click', () => { createToolInterface(); const toolPanel = document.querySelector('#whiteBackgroundTool > div'); toolPanel.style.display = toolPanel.style.display === 'none' ? 'block' : 'none'; }); document.body.appendChild(button); } // 等待页面加载完成后创建按钮 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', createLaunchButton); } else { createLaunchButton(); } })();