一键生成网页二维码脚本

发布于 1 天前  51 次阅读


本文于 2025年8月16日 10:00 更新,注意查看最新内容

一个经常使用的网站需要搭配手机一起使用,但是由于屏蔽了右键,导致Edge的QR码无法调用,于是用AI写了一个脚本,会在每个页面左边的正中间生成一个二维码图标,点击之后可以一键生成当前网页的二维码。

// ==UserScript==
// @name         页面二维码生成器(完整缓存版)
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  生成当前页面二维码,带完善缓存机制确保图标正常显示
// @author       某知名AI
// @match        *://*/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @connect      cdnjs.cloudflare.com
// @require      https://cdn.jsdelivr.net/npm/qrcode@1.5.1/build/qrcode.min.js
// ==/UserScript==

(function() {
    'use strict';

    // 缓存配置 - 7天有效期
    const CACHE_EXPIRY_DAYS = 7;
    const FONT_CACHE_KEY = 'qrcodeFontCache';
    const FONT_CACHE_TIMESTAMP = 'qrcodeFontTimestamp';

    // 所需字体文件URL
    const fontUrls = {
        woff2: 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/fonts/fontawesome-webfont.woff2?v=4.7.0',
        woff: 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/fonts/fontawesome-webfont.woff?v=4.7.0'
    };

    // 检查缓存是否有效
    function isCacheValid() {
        const timestamp = GM_getValue(FONT_CACHE_TIMESTAMP, 0);
        const now = new Date().getTime();
        const expiryTime = CACHE_EXPIRY_DAYS * 24 * 60 * 60 * 1000;
        return timestamp + expiryTime > now;
    }

    // 从缓存加载字体
    function loadFromCache() {
        const cachedFonts = GM_getValue(FONT_CACHE_KEY, null);
        if (cachedFonts) {
            injectFontStyles(cachedFonts);
            return true;
        }
        return false;
    }

    // 下载字体并缓存
    function downloadAndCacheFonts() {
        // 优先尝试woff2格式,兼容性更好
        fetchFont(fontUrls.woff2, 'woff2')
            .catch(() => {
                // 如果woff2失败,尝试woff格式
                return fetchFont(fontUrls.woff, 'woff');
            })
            .then(({data, format}) => {
                const fontData = {data, format};
                // 存储到缓存
                GM_setValue(FONT_CACHE_KEY, fontData);
                GM_setValue(FONT_CACHE_TIMESTAMP, new Date().getTime());
                injectFontStyles(fontData);
            })
            .catch(() => {
                // 所有字体加载失败时使用基础样式 fallback
                injectFallbackStyles();
            });
    }

    // 下载字体文件
    function fetchFont(url, format) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'GET',
                url: url,
                responseType: 'arraybuffer',
                onload: function(response) {
                    if (response.status === 200 && response.response) {
                        // 转换为base64
                        const base64Data = btoa(
                            new Uint8Array(response.response).reduce(
                                (data, byte) => data + String.fromCharCode(byte),
                                ''
                            )
                        );
                        resolve({data: base64Data, format});
                    } else {
                        reject(new Error(`Failed to load font: ${response.status}`));
                    }
                },
                onerror: function() {
                    reject(new Error('Network error while loading font'));
                },
                ontimeout: function() {
                    reject(new Error('Font loading timed out'));
                }
            });
        });
    }

    // 注入字体样式
    function injectFontStyles(fontData) {
        const style = document.createElement('style');
        style.textContent = `
            @font-face {
                font-family: 'FontAwesome';
                src: url('data:application/font-${fontData.format};base64,${fontData.data}') format('${fontData.format}');
                font-weight: normal;
                font-style: normal;
            }
            .fa {
                display: inline-block;
                font: normal normal normal 14px/1 FontAwesome;
                font-size: inherit;
                text-rendering: auto;
                -webkit-font-smoothing: antialiased;
                -moz-osx-font-smoothing: grayscale;
            }
            .fa-qrcode:before {
                content: "\\f029";
            }
            .fa-times:before {
                content: "\\f00d";
            }
        `;
        document.head.appendChild(style);
    }

    // 字体加载失败时的 fallback 样式
    function injectFallbackStyles() {
        const style = document.createElement('style');
        style.textContent = `
            .fa-qrcode:before { content: "🔳"; }
            .fa-times:before { content: "✕"; }
        `;
        document.head.appendChild(style);
    }

    // 创建UI元素
    function createUI() {
        // 创建按钮容器
        const buttonContainer = document.createElement('div');
        buttonContainer.style.position = 'fixed';
        buttonContainer.style.left = '10px';
        buttonContainer.style.top = '50%';
        buttonContainer.style.transform = 'translateY(-50%)';
        buttonContainer.style.zIndex = '9999';
        buttonContainer.style.display = 'flex';
        buttonContainer.style.flexDirection = 'column';
        buttonContainer.style.alignItems = 'center';

        // 创建二维码按钮
        const qrButton = document.createElement('button');
        qrButton.innerHTML = '<i class="fa fa-qrcode"></i>';

        // 创建关闭按钮(默认隐藏)
        const hideButton = document.createElement('button');
        hideButton.innerHTML = '<i class="fa fa-times"></i>';
        hideButton.style.position = 'absolute';
        hideButton.style.top = '-10px';
        hideButton.style.right = '-10px';
        hideButton.style.width = '24px';
        hideButton.style.height = '24px';
        hideButton.style.borderRadius = '50%';
        hideButton.style.backgroundColor = '#f44336';
        hideButton.style.color = 'white';
        hideButton.style.border = 'none';
        hideButton.style.cursor = 'pointer';
        hideButton.style.boxShadow = '0 1px 3px rgba(0,0,0,0.2)';
        hideButton.style.display = 'none'; // 默认隐藏
        hideButton.style.alignItems = 'center';
        hideButton.style.justifyContent = 'center';
        hideButton.style.fontSize = '12px';
        hideButton.title = '彻底隐藏二维码按钮(刷新页面可恢复)';

        // 二维码按钮样式
        qrButton.style.width = '40px';
        qrButton.style.height = '40px';
        qrButton.style.borderRadius = '8px';
        qrButton.style.backgroundColor = 'rgba(255, 255, 255, 0.8)';
        qrButton.style.color = '#333';
        qrButton.style.border = '1px solid #ddd';
        qrButton.style.cursor = 'pointer';
        qrButton.style.boxShadow = '0 2px 5px rgba(0,0,0,0.1)';
        qrButton.style.transition = 'all 0.3s ease';
        qrButton.style.display = 'flex';
        qrButton.style.alignItems = 'center';
        qrButton.style.justifyContent = 'center';
        qrButton.style.fontSize = '18px';
        qrButton.title = '生成当前页面二维码';

        // 按钮容器悬停效果
        buttonContainer.addEventListener('mouseover', () => {
            qrButton.style.width = '50px';
            qrButton.style.backgroundColor = 'white';
            qrButton.style.boxShadow = '0 3px 8px rgba(0,0,0,0.2)';
            hideButton.style.display = 'flex';
        });

        buttonContainer.addEventListener('mouseout', () => {
            qrButton.style.width = '40px';
            qrButton.style.backgroundColor = 'rgba(255, 255, 255, 0.8)';
            qrButton.style.boxShadow = '0 2px 5px rgba(0,0,0,0.1)';
            hideButton.style.display = 'none';
        });

        // 弹窗元素(延迟创建)
        let qrModal, qrContainer, closeButton, urlText;

        function initModal() {
            if (qrModal) return;

            // 创建二维码弹窗容器
            qrModal = document.createElement('div');
            qrModal.style.position = 'fixed';
            qrModal.style.top = '0';
            qrModal.style.left = '0';
            qrModal.style.width = '100%';
            qrModal.style.height = '100%';
            qrModal.style.backgroundColor = 'rgba(0,0,0,0.7)';
            qrModal.style.display = 'none';
            qrModal.style.justifyContent = 'center';
            qrModal.style.alignItems = 'center';
            qrModal.style.zIndex = '10000';
            qrModal.style.flexDirection = 'column';
            qrModal.style.backdropFilter = 'blur(3px)';

            // 创建二维码图片容器
            qrContainer = document.createElement('div');
            qrContainer.style.backgroundColor = 'white';
            qrContainer.style.padding = '20px';
            qrContainer.style.borderRadius = '10px';
            qrContainer.style.boxShadow = '0 0 20px rgba(0,0,0,0.5)';
            qrContainer.style.textAlign = 'center';
            qrContainer.style.maxWidth = '90%';
            qrContainer.style.transform = 'scale(0.95)';
            qrContainer.style.transition = 'transform 0.3s ease';

            // 创建关闭按钮
            closeButton = document.createElement('button');
            closeButton.innerHTML = '<i class="fa fa-times"></i> 关闭';
            closeButton.style.marginTop = '20px';
            closeButton.style.padding = '8px 16px';
            closeButton.style.backgroundColor = '#666';
            closeButton.style.color = 'white';
            closeButton.style.border = 'none';
            closeButton.style.borderRadius = '5px';
            closeButton.style.cursor = 'pointer';
            closeButton.style.fontSize = '14px';
            closeButton.style.transition = 'background-color 0.2s';

            closeButton.addEventListener('mouseover', () => {
                closeButton.style.backgroundColor = '#333';
            });

            closeButton.addEventListener('mouseout', () => {
                closeButton.style.backgroundColor = '#666';
            });

            // 页面URL文本显示
            urlText = document.createElement('p');
            urlText.style.wordBreak = 'break-all';
            urlText.style.maxWidth = '300px';
            urlText.style.marginTop = '15px';
            urlText.style.fontSize = '14px';
            urlText.style.color = '#555';

            // 组装弹窗
            qrContainer.appendChild(urlText);
            qrModal.appendChild(qrContainer);
            qrModal.appendChild(closeButton);
            document.body.appendChild(qrModal);

            // 弹窗事件监听
            closeButton.addEventListener('click', () => {
                qrModal.style.display = 'none';
            });

            qrModal.addEventListener('click', (e) => {
                if (e.target === qrModal) {
                    qrModal.style.display = 'none';
                }
            });

            document.addEventListener('keydown', (e) => {
                if (e.key === 'Escape' && qrModal.style.display === 'flex') {
                    qrModal.style.display = 'none';
                }
            });
        }

        // 生成二维码函数
        function generateQRCode() {
            initModal(); // 首次点击时初始化弹窗

            // 清空之前的二维码
            while (qrContainer.firstChild) {
                if (qrContainer.firstChild.tagName === 'IMG' || qrContainer.firstChild.tagName === 'CANVAS') {
                    qrContainer.removeChild(qrContainer.firstChild);
                } else {
                    break;
                }
            }

            // 重置容器缩放
            qrContainer.style.transform = 'scale(0.95)';

            // 获取当前页面URL
            const currentUrl = window.location.href;
            urlText.textContent = currentUrl;

            // 生成二维码
            QRCode.toCanvas(currentUrl, {
                width: 300,
                margin: 1
            }, function (error, canvas) {
                if (error) {
                    console.error(error);
                    alert('生成二维码失败: ' + error.message);
                    return;
                }
                qrContainer.insertBefore(canvas, qrContainer.firstChild);
            });

            // 显示弹窗
            qrModal.style.display = 'flex';
        }

        // 彻底隐藏二维码按钮
        function hideQRButtonCompletely() {
            if (buttonContainer.parentNode === document.body) {
                document.body.removeChild(buttonContainer);
            }
        }

        // 绑定事件
        qrButton.addEventListener('click', generateQRCode);
        hideButton.addEventListener('click', hideQRButtonCompletely);

        // 添加到页面
        buttonContainer.appendChild(qrButton);
        buttonContainer.appendChild(hideButton);
        document.body.appendChild(buttonContainer);
    }

    // 初始化流程 - 优先使用缓存
    if (isCacheValid() && loadFromCache()) {
        // 缓存有效且加载成功,创建UI
        createUI();
    } else {
        // 缓存无效或加载失败,重新下载
        downloadAndCacheFonts();
        // 无论字体加载结果如何,都创建UI(确保基本功能可用)
        createUI();
    }
})();

 


这短短的一生,我们最终都会失去。