// ==UserScript== // @name 图片上传工具(替换摄像头拍到的图片) // @namespace http://tampermonkey.net/ // @version 0.2 // @description 替换网页拍摄的照片(支持img、canvas、视频帧转换场景)丨支持广东教育考试服务平台高考报名证件照上传 // @author TianMiao // @match *://*/* // @match *://pg.eeagd.edu.cn/* // @grant none // @run-at document-end // ==/UserScript== (function() { 'use strict'; let selectedImage = null; let selectedFile = null; // 存储原始文件对象,用于替换file输入 // 创建图片选择按钮 function createImageSelector() { const container = document.createElement('div'); container.style.position = 'fixed'; container.style.top = '20px'; container.style.left = '50%'; container.style.transform = 'translateX(-50%)'; container.style.zIndex = '999999'; container.style.padding = '10px 15px'; container.style.backgroundColor = 'rgba(0, 0, 0, 0.8)'; container.style.borderRadius = '8px'; container.style.boxShadow = '0 2px 15px rgba(0, 0, 0, 0.5)'; container.style.transition = 'all 0.3s ease'; const text = document.createElement('span'); text.textContent = '选择替换图片: '; text.style.color = 'white'; text.style.marginRight = '10px'; text.style.fontFamily = 'Arial, sans-serif'; const fileInput = document.createElement('input'); fileInput.type = 'file'; fileInput.accept = 'image/*'; fileInput.style.padding = '5px'; fileInput.style.borderRadius = '4px'; fileInput.style.cursor = 'pointer'; // 监听文件选择,同时保存原始文件对象 fileInput.addEventListener('change', function(e) { const file = e.target.files[0]; if (file) { selectedFile = file; // 保存原始文件 const reader = new FileReader(); reader.onload = function(event) { selectedImage = event.target.result; statusText.textContent = `已选择: ${file.name}`; statusText.style.color = '#4CAF50'; // 选择后立即检查现有元素并替换 replaceExistingMedia(); }; reader.readAsDataURL(file); } }); const statusText = document.createElement('span'); statusText.textContent = '未选择图片'; statusText.style.color = '#ff9800'; statusText.style.marginLeft = '10px'; statusText.style.fontSize = '14px'; container.appendChild(text); container.appendChild(fileInput); container.appendChild(statusText); document.body.appendChild(container); // 鼠标悬停效果 container.addEventListener('mouseenter', () => { container.style.transform = 'translateX(-50%) scale(1.05)'; }); container.addEventListener('mouseleave', () => { container.style.transform = 'translateX(-50%) scale(1)'; }); } // 替换canvas内容为选中的图片 function replaceCanvas(canvas) { if (!selectedImage || !canvas.getContext) return; const ctx = canvas.getContext('2d'); const img = new Image(); img.onload = function() { // 保持canvas原始尺寸 canvas.width = canvas.offsetWidth; canvas.height = canvas.offsetHeight; ctx.drawImage(img, 0, 0, canvas.width, canvas.height); }; img.src = selectedImage; } // 替换file输入框的内容(用于上传场景) function replaceFileInputs() { if (!selectedFile) return; // 查找可能用于存储拍摄图片的file输入(通常是隐藏的) document.querySelectorAll('input[type="file"]').forEach(input => { // 排除我们自己创建的选择器 if (input.accept.includes('image') && !input.parentNode.style.zIndex) { // 用DataTransfer模拟文件选择 const dataTransfer = new DataTransfer(); dataTransfer.items.add(selectedFile); input.files = dataTransfer.files; // 触发change事件,让页面感知文件变化 input.dispatchEvent(new Event('change', { bubbles: true })); } }); } // 替换现有媒体元素(img/canvas/video) function replaceExistingMedia() { if (!selectedImage) return; // 处理img元素 document.querySelectorAll('img').forEach(img => { const isCameraImage = img.src.startsWith('blob:') || (img.src.startsWith('data:image/') && img.src.length > 1000); if (isCameraImage) { img.src = selectedImage; img.srcset = selectedImage; // 处理响应式图片 img.style.objectFit = 'cover'; // 保持填充方式 } }); // 处理canvas元素(很多拍摄场景用canvas预览) document.querySelectorAll('canvas').forEach(canvas => { // 判断是否是摄像头拍摄的canvas(通常有频繁绘制) if (canvas.width > 0 && canvas.height > 0) { replaceCanvas(canvas); } }); // 处理file输入框 replaceFileInputs(); } // 监控页面元素变化并替换 function monitorAndReplace() { const observer = new MutationObserver((mutations) => { mutations.forEach(mutation => { // 处理新增节点 mutation.addedNodes.forEach(node => { if (node.tagName === 'IMG' && selectedImage) { const isCameraImage = node.src.startsWith('blob:') || (node.src.startsWith('data:image/') && node.src.length > 1000); if (isCameraImage) { node.src = selectedImage; node.srcset = selectedImage; } } else if (node.tagName === 'CANVAS' && selectedImage) { replaceCanvas(node); } }); // 处理属性变化(如已有img的src更新) if (mutation.type === 'attributes' && mutation.target.tagName === 'IMG') { const img = mutation.target; const isCameraImage = img.src.startsWith('blob:') || (img.src.startsWith('data:image/') && img.src.length > 1000); if (selectedImage && isCameraImage) { img.src = selectedImage; img.srcset = selectedImage; } } }); }); // 观察范围:所有子节点及属性变化 observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['src', 'srcset'] // 重点监控图片源属性 }); // 监听可能的拍摄按钮点击(增强替换时机) document.addEventListener('click', (e) => { const btnText = e.target.textContent || e.target.value || ''; if (btnText.includes('拍摄') || btnText.includes('拍照') || btnText.includes('确认') && selectedImage) { // 点击拍摄按钮后延迟替换(等待拍摄完成) setTimeout(replaceExistingMedia, 500); } }); } // 初始化 function init() { createImageSelector(); monitorAndReplace(); // 页面加载完成后再检查一次 setTimeout(replaceExistingMedia, 2000); } if (document.readyState === 'complete') { init(); } else { window.addEventListener('load', init); } })();