// ==UserScript== // @name 二维码识别 // @version 1.0 // @description 1.点击菜单栏里的"识别二维码"就可以识别当前页面中的二维码(快捷键:ctrl+q); 2.按住ctrl键,右键点击图片,即可识别该图片中的二维码; 3.支持上传二维码图片识别 // @author IIIStudio // @license MIT // @homepageURL https://cnb.cool/IIIStudio/Greasemonkey/QRCODE/ // @include * // @require https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js // @require https://cdn.jsdelivr.net/npm/jsqr@1.4.0/dist/jsQR.js // @grant GM_registerMenuCommand // @grant GM_xmlhttpRequest // @connect * // ==/UserScript== (function () { "use strict"; var css = ` #QRCODE_DECODER { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 999999; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 12px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); min-width: 320px; max-width: 400px; overflow: hidden; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; } #QRCODE_DECODER::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 4px; background: linear-gradient(90deg, #ff6b6b, #ffd93d, #6bcf7f, #4d96ff); } #QRCODE_DECODER .close { position: absolute; right: 12px; top: 12px; width: 24px; height: 24px; border-radius: 50%; color: #353535; text-decoration: none; font-size: 18px; font-weight: bold; display: flex; align-items: center; justify-content: center; transition: all 0.3s ease; z-index: 1; } #QRCODE_DECODER .close:hover { background: rgba(255, 255, 255, 0.3); transform: rotate(90deg); } #QRCODE_DECODER .result { padding: 30px 25px 25px; background: white; margin: 4px; border-radius: 8px; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); } #QRCODE_DECODER .result-header { font-size: 16px; font-weight: 600; color: #2d3748; text-align: center; margin-bottom: 20px; display: flex; align-items: center; justify-content: center; gap: 8px; } #QRCODE_DECODER .result-header::before { content: '🔍'; font-size: 18px; } #QRCODE_DECODER .content-text { background: #f7fafc; border: 1px solid #e2e8f0; border-radius: 8px; padding: 15px; margin-bottom: 20px; word-break: break-all; font-size: 14px; line-height: 1.5; color: #4a5568; max-height: 150px; overflow-y: auto; transition: all 0.3s ease; } #QRCODE_DECODER .content-text:hover { border-color: #667eea; box-shadow: 0 2px 8px rgba(102, 126, 234, 0.1); } #QRCODE_DECODER .action-buttons { display: flex; gap: 12px; justify-content: center; } #QRCODE_DECODER .action-buttons button { padding: 10px 20px; border: none; border-radius: 6px; font-size: 14px; font-weight: 500; cursor: pointer; transition: all 0.3s ease; flex: 1; display: flex; align-items: center; justify-content: center; gap: 6px; min-width: 80px; } #QRCODE_DECODER #copyQRContent { background: linear-gradient(135deg, #667eea, #764ba2); color: white; } #QRCODE_DECODER #copyQRContent:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4); } #QRCODE_DECODER #copyQRContent:active { transform: translateY(0); } #QRCODE_DECODER #copyQRContent.copied { background: linear-gradient(135deg, #48bb78, #38a169); } #QRCODE_DECODER #openQRContent { background: linear-gradient(135deg, #ff6b6b, #ee5a52); color: white; } #QRCODE_DECODER #openQRContent:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(255, 107, 107, 0.4); } #QRCODE_DECODER #openQRContent:active { transform: translateY(0); } #QRCODE_DECODER .loading { padding: 40px 20px; text-align: center; color: #4a5568; font-size: 16px; display: flex; flex-direction: column; align-items: center; gap: 15px; } #QRCODE_DECODER .loading::before { content: '⏳'; font-size: 24px; animation: spin 1s linear infinite; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } #QRCODE_DECODER .error { color: #e53e3e; text-align: center; padding: 30px 20px; font-size: 14px; display: flex; flex-direction: column; align-items: center; gap: 10px; } #QRCODE_DECODER .error::before { content: '❌'; font-size: 24px; } #QRCODE_DECODER .upload-section { padding: 20px; background: white; margin: 4px; border-radius: 8px; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); text-align: center; } #QRCODE_DECODER .upload-area { border: 2px dashed #cbd5e0; border-radius: 8px; padding: 30px 20px; cursor: pointer; transition: all 0.3s ease; background: #f7fafc; } #QRCODE_DECODER .upload-area:hover { border-color: #667eea; background: #edf2f7; } #QRCODE_DECODER .upload-icon { font-size: 48px; margin-bottom: 15px; color: #a0aec0; } #QRCODE_DECODER .upload-text { color: #4a5568; font-size: 14px; margin-bottom: 10px; } #QRCODE_DECODER .upload-hint { color: #718096; font-size: 12px; } #QRCODE_DECODER #fileInput { display: none; } #QRCODE_DECODER .upload-button { background: linear-gradient(135deg, #4299e1, #3182ce); color: white; border: none; border-radius: 6px; padding: 10px 20px; font-size: 14px; cursor: pointer; transition: all 0.3s ease; margin-top: 15px; } #QRCODE_DECODER .upload-button:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(66, 153, 225, 0.4); } `; var $ = jQuery; var currentQRContent = ''; function getDecoderLayer() { if ($("#QRCODE_DECODER").length) { return $("#QRCODE_DECODER"); } var layer = $('
').appendTo($("body")); var resultDiv = $('').appendTo(layer); var closeBtn = $('×').appendTo(layer); closeBtn.on("click", function() { layer.remove(); }); // 点击背景关闭 layer.on("click", function(e) { if (e.target === this) { layer.remove(); } }); return layer; } function decodeQRCodeFromImage(src, callback) { // 检查是否是跨域图片 const isCrossOrigin = () => { try { const imgUrl = new URL(src, window.location.href); const currentUrl = new URL(window.location.href); return imgUrl.origin !== currentUrl.origin; } catch (e) { return true; } }; if (isCrossOrigin()) { // 使用GM_xmlhttpRequest处理跨域图片 GM_xmlhttpRequest({ method: "GET", url: src, responseType: "blob", onload: function(response) { const blob = response.response; const url = URL.createObjectURL(blob); const img = new Image(); img.onload = function() { decodeImageToQRCode(img); URL.revokeObjectURL(url); // 清理内存 }; img.onerror = function() { callback(new Error("图片加载失败")); }; img.src = url; }, onerror: function() { callback(new Error("无法加载跨域图片")); } }); } else { // 同源图片直接处理 const img = new Image(); img.crossOrigin = "Anonymous"; img.onload = function() { decodeImageToQRCode(img); }; img.onerror = function() { callback(new Error("图片加载失败")); }; img.src = src; } function decodeImageToQRCode(img) { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.width; canvas.height = img.height; ctx.drawImage(img, 0, 0); try { const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const code = jsQR(imageData.data, imageData.width, imageData.height); if (code) { callback(null, code.data); } else { callback(new Error("未检测到二维码")); } } catch (e) { callback(e); } } } function decodeQRCodeFromFile(file, callback) { const reader = new FileReader(); reader.onload = function(e) { const img = new Image(); img.onload = function() { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.width; canvas.height = img.height; ctx.drawImage(img, 0, 0); try { const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const code = jsQR(imageData.data, imageData.width, imageData.height); if (code) { callback(null, code.data); } else { callback(new Error("未检测到二维码")); } } catch (e) { callback(e); } }; img.onerror = function() { callback(new Error("图片加载失败")); }; img.src = e.target.result; }; reader.onerror = function() { callback(new Error("文件读取失败")); }; reader.readAsDataURL(file); } function copyToClipboard(text) { navigator.clipboard.writeText(text).then(function() { // 复制成功反馈 const copyBtn = $('#copyQRContent'); const originalText = copyBtn.html(); copyBtn.addClass('copied').html('✅ 已复制'); setTimeout(() => { copyBtn.removeClass('copied').html(originalText); }, 2000); }).catch(function() { // 降级方案 const textArea = document.createElement("textarea"); textArea.value = text; document.body.appendChild(textArea); textArea.select(); const success = document.execCommand("copy"); document.body.removeChild(textArea); if (success) { const copyBtn = $('#copyQRContent'); const originalText = copyBtn.html(); copyBtn.addClass('copied').html('✅ 已复制'); setTimeout(() => { copyBtn.removeClass('copied').html(originalText); }, 2000); } }); } function openURL(url) { if (/^https?:\/\//.test(url)) { window.open(url, '_blank'); } else { alert('这不是一个有效的网址'); } } function showQRResult(content) { currentQRContent = content; const layer = getDecoderLayer(); layer.find(".result") .html('