Skip to content

Instantly share code, notes, and snippets.

@engalar
Last active November 21, 2025 07:09
Show Gist options
  • Select an option

  • Save engalar/5a8f2a401402acf0ace114a6f51c5ce1 to your computer and use it in GitHub Desktop.

Select an option

Save engalar/5a8f2a401402acf0ace114a6f51c5ce1 to your computer and use it in GitHub Desktop.
网页元素截图工具
// ==UserScript==
// @name 网页元素截图工具
// @namespace http://tampermonkey.net/
// @version 1.2
// @description 直接利用 @require 加载库,解决 window.htmlToImage 为 undefined 的问题
// @author YourName
// @match *://*/*
// @grant GM_registerMenuCommand
// @grant GM_addStyle
// @require https://cdnjs.cloudflare.com/ajax/libs/html-to-image/1.11.11/html-to-image.min.js
// @downloadURL https://gist.github.com/engalar/5a8f2a401402acf0ace114a6f51c5ce1/raw/snap.user.js
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
// =================================================================
// 1. 核心截图逻辑 (大幅简化)
// =================================================================
const captureElement = async function (element, fileName) {
try {
// 检查库是否加载成功 (@require 会在脚本运行前加载它)
// 注意:在 @require 模式下,库通常挂载在 window 或全局作用域下
// 如果网页有 define,油猴通常有机制隔离,或者库会挂载到 globalThis
const lib = window.htmlToImage || htmlToImage;
if (!lib) {
throw new Error('html-to-image 库未找到,可能是 @require 加载失败');
}
const toast = showToast('正在生成截图,请稍候...');
const config = {
quality: 0.95,
backgroundColor: '#ffffff',
filter: (node) => (node.id !== 'tm-screenshot-toast')
};
const dataUrl = await lib.toPng(element, config);
const link = document.createElement('a');
link.download = fileName || `screenshot_${Date.now()}.png`;
link.href = dataUrl;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
toast.innerText = '截图成功!已开始下载';
setTimeout(() => toast.remove(), 2000);
} catch (error) {
console.error('截图失败:', error);
showToast('截图失败: ' + error.message, true);
}
};
// =================================================================
// 2. 交互选择器逻辑 (保持不变)
// =================================================================
let isSelecting = false;
let currentHighlight = null;
let highlightStyleElement = null;
function activateSelector() {
if (isSelecting) return;
isSelecting = true;
showToast('进入截图模式:请点击要截取的元素 (右键取消)');
const css = `
.tm-screenshot-highlight {
outline: 4px solid #ff4757 !important;
outline-offset: -4px !important;
cursor: crosshair !important;
transition: all 0.1s;
}
`;
highlightStyleElement = GM_addStyle(css);
document.addEventListener('mouseover', onMouseOver, true);
document.addEventListener('mouseout', onMouseOut, true);
document.addEventListener('click', onClick, true);
document.addEventListener('contextmenu', onCancel, true);
}
function cleanup() {
isSelecting = false;
if (currentHighlight) {
currentHighlight.classList.remove('tm-screenshot-highlight');
currentHighlight = null;
}
if (highlightStyleElement) {
highlightStyleElement.remove();
highlightStyleElement = null;
}
document.removeEventListener('mouseover', onMouseOver, true);
document.removeEventListener('mouseout', onMouseOut, true);
document.removeEventListener('click', onClick, true);
document.removeEventListener('contextmenu', onCancel, true);
}
function onMouseOver(e) {
if (!isSelecting) return;
e.stopPropagation();
if(e.target.id === 'tm-screenshot-toast') return;
if (currentHighlight && currentHighlight !== e.target) {
currentHighlight.classList.remove('tm-screenshot-highlight');
}
currentHighlight = e.target;
currentHighlight.classList.add('tm-screenshot-highlight');
}
function onMouseOut(e) {
if (!isSelecting) return;
if (e.target === currentHighlight) {
e.target.classList.remove('tm-screenshot-highlight');
currentHighlight = null;
}
}
function onClick(e) {
if (!isSelecting) return;
e.preventDefault();
e.stopPropagation();
const target = e.target;
target.classList.remove('tm-screenshot-highlight');
captureElement(target);
cleanup();
}
function onCancel(e) {
if (!isSelecting) return;
e.preventDefault();
showToast('已取消截图');
cleanup();
}
function showToast(text, isError = false) {
let toast = document.getElementById('tm-screenshot-toast');
if (!toast) {
toast = document.createElement('div');
toast.id = 'tm-screenshot-toast';
Object.assign(toast.style, {
position: 'fixed',
top: '20px',
left: '50%',
transform: 'translateX(-50%)',
padding: '10px 20px',
borderRadius: '5px',
color: '#fff',
fontSize: '14px',
fontFamily: 'sans-serif',
zIndex: '2147483647',
pointerEvents: 'none',
boxShadow: '0 2px 10px rgba(0,0,0,0.2)'
});
document.body.appendChild(toast);
}
toast.style.backgroundColor = isError ? '#ff4757' : '#2ed573';
toast.innerText = text;
return toast;
}
GM_registerMenuCommand("📷 截取指定元素", activateSelector);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment