Greasy Fork

一键文本转图片

按下 Alt+I 时将选中文字转为图片并复制到剪贴板

目前为 2024-11-25 提交的版本。查看 最新版本

// ==UserScript==
// @name         一键文本转图片
// @namespace    mailto:[email protected]
// @version      1.5
// @description  按下 Alt+I 时将选中文字转为图片并复制到剪贴板
// @author       Cycle Bai
// @license      LGPL
// @match        *://*/*
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==

(function() {
    'use strict';

    // 配置选项
    const config = {
        fontSize: 16,
        fontFamily: 'Arial, "Microsoft YaHei", sans-serif',
        padding: 20,
        maxWidth: 800,
        lineHeight: 1.5,
        backgroundColor: '#ffffff',
        textColor: '#333333',
        borderRadius: 8,
        shadowColor: 'rgba(0, 0, 0, 0.1)',
    };

    // 获取选中的文本
    function getSelectedText() {
        const activeElement = document.activeElement;
        const selection = window.getSelection().toString().trim();

        if (selection) return selection;

        if (activeElement.tagName === 'TEXTAREA' || activeElement.tagName === 'INPUT') {
            return activeElement.value.substring(
                activeElement.selectionStart,
                activeElement.selectionEnd
            ).trim();
        }

        return '';
    }

    // 优化的文本换行处理
    function wrapText(context, text, maxWidth) {
        const characters = Array.from(text);
        let lines = [];
        let currentLine = '';

        for (let char of characters) {
            const testLine = currentLine + char;
            const metrics = context.measureText(testLine);

            if (metrics.width > maxWidth - (config.padding * 2)) {
                lines.push(currentLine);
                currentLine = char;
            } else {
                currentLine = testLine;
            }
        }

        if (currentLine) {
            lines.push(currentLine);
        }

        return lines;
    }

    // 创建并配置画布
    function setupCanvas(lines) {
        const canvas = document.createElement('canvas');
        const context = canvas.getContext('2d');

        // 设置字体
        context.font = `${config.fontSize}px ${config.fontFamily}`;

        // 计算画布尺寸
        const lineHeight = config.fontSize * config.lineHeight;
        const width = config.maxWidth;
        const height = lines.length * lineHeight + (config.padding * 2);

        // 设置画布尺寸(考虑设备像素比以提高清晰度)
        const scale = window.devicePixelRatio || 1;
        canvas.width = width * scale;
        canvas.height = height * scale;
        canvas.style.width = width + 'px';
        canvas.style.height = height + 'px';

        // 缩放上下文以匹配设备像素比
        context.scale(scale, scale);

        return { canvas, context, lineHeight };
    }

    // 绘制图片
    function drawImage(canvas, context, lines, lineHeight) {
        // 绘制背景
        context.fillStyle = config.backgroundColor;
        context.fillRect(0, 0, canvas.width, canvas.height);

        // 添加圆角
        context.beginPath();
        context.roundRect(0, 0, canvas.width, canvas.height, config.borderRadius);
        context.clip();

        // 添加阴影
        context.shadowColor = config.shadowColor;
        context.shadowBlur = 10;
        context.shadowOffsetX = 0;
        context.shadowOffsetY = 2;

        // 绘制文本
        context.fillStyle = config.textColor;
        context.font = `${config.fontSize}px ${config.fontFamily}`;
        context.textBaseline = 'middle';

        lines.forEach((line, i) => {
            const y = config.padding + (i + 0.5) * lineHeight;
            context.fillText(line, config.padding, y);
        });
    }

    // 显示通知
    function showNotification(message, type = 'info') {
        const notification = document.createElement('div');
        notification.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            padding: 12px 24px;
            background: ${type === 'success' ? '#4caf50' : type === 'warning' ? '#ff9800' : '#f44336'};
            color: white;
            border-radius: 4px;
            font-size: 14px;
            z-index: 9999;
            box-shadow: 0 2px 5px rgba(0,0,0,0.2);
            animation: fadeInOut 3s ease-in-out;
        `;

        notification.textContent = message;
        document.body.appendChild(notification);

        setTimeout(() => {
            notification.remove();
        }, 3000);
    }

    // 主要事件处理函数
    async function handleKeyPress(event) {
        if (!(event.altKey && event.key.toLowerCase() === 'i')) return;

        const selection = getSelectedText();
        if (!selection) {
            showNotification('请先选中文本!', 'warning');
            return;
        }

        try {
            const context = document.createElement('canvas').getContext('2d');
            context.font = `${config.fontSize}px ${config.fontFamily}`;

            const lines = selection.split('\n').flatMap(line =>
                wrapText(context, line, config.maxWidth)
            );

            const { canvas, context: finalContext, lineHeight } = setupCanvas(lines);
            drawImage(canvas, finalContext, lines, lineHeight);

            const blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/png'));
            await navigator.clipboard.write([
                new ClipboardItem({ 'image/png': blob })
            ]);

            showNotification('图片已复制到剪贴板!', 'success');
        } catch (error) {
            console.error('转换失败:', error);
            showNotification('转换失败,请检查权限或浏览器兼容性。', 'error');
        }
    }

    // 注册事件监听器
    document.addEventListener('keydown', handleKeyPress);

    // 添加样式
    const style = document.createElement('style');
    style.textContent = `
        @keyframes fadeInOut {
            0% { opacity: 0; transform: translateY(-20px); }
            10% { opacity: 1; transform: translateY(0); }
            90% { opacity: 1; transform: translateY(0); }
            100% { opacity: 0; transform: translateY(-20px); }
        }
    `;
    document.head.appendChild(style);
})();// ==UserScript==
// @name         一键文本转图片
// @namespace    https://iscyclebai.com
// @version      1.5
// @description  按下 Alt+I 时将选中文字转为图片并复制到剪贴板
// @author       Cycle Bai
// @match        *://*/*
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==

(function() {
    'use strict';

    // 配置选项
    const config = {
        fontSize: 16,
        fontFamily: 'Arial, "Microsoft YaHei", sans-serif',
        padding: 20,
        maxWidth: 800,
        lineHeight: 1.5,
        backgroundColor: '#ffffff',
        textColor: '#333333',
        borderRadius: 8,
        shadowColor: 'rgba(0, 0, 0, 0.1)',
    };

    // 获取选中的文本
    function getSelectedText() {
        const activeElement = document.activeElement;
        const selection = window.getSelection().toString().trim();

        if (selection) return selection;

        if (activeElement.tagName === 'TEXTAREA' || activeElement.tagName === 'INPUT') {
            return activeElement.value.substring(
                activeElement.selectionStart,
                activeElement.selectionEnd
            ).trim();
        }

        return '';
    }

    // 优化的文本换行处理
    function wrapText(context, text, maxWidth) {
        const characters = Array.from(text);
        let lines = [];
        let currentLine = '';

        for (let char of characters) {
            const testLine = currentLine + char;
            const metrics = context.measureText(testLine);

            if (metrics.width > maxWidth - (config.padding * 2)) {
                lines.push(currentLine);
                currentLine = char;
            } else {
                currentLine = testLine;
            }
        }

        if (currentLine) {
            lines.push(currentLine);
        }

        return lines;
    }

    // 创建并配置画布
    function setupCanvas(lines) {
        const canvas = document.createElement('canvas');
        const context = canvas.getContext('2d');

        // 设置字体
        context.font = `${config.fontSize}px ${config.fontFamily}`;

        // 计算画布尺寸
        const lineHeight = config.fontSize * config.lineHeight;
        const width = config.maxWidth;
        const height = lines.length * lineHeight + (config.padding * 2);

        // 设置画布尺寸(考虑设备像素比以提高清晰度)
        const scale = window.devicePixelRatio || 1;
        canvas.width = width * scale;
        canvas.height = height * scale;
        canvas.style.width = width + 'px';
        canvas.style.height = height + 'px';

        // 缩放上下文以匹配设备像素比
        context.scale(scale, scale);

        return { canvas, context, lineHeight };
    }

    // 绘制图片
    function drawImage(canvas, context, lines, lineHeight) {
        // 绘制背景
        context.fillStyle = config.backgroundColor;
        context.fillRect(0, 0, canvas.width, canvas.height);

        // 添加圆角
        context.beginPath();
        context.roundRect(0, 0, canvas.width, canvas.height, config.borderRadius);
        context.clip();

        // 添加阴影
        context.shadowColor = config.shadowColor;
        context.shadowBlur = 10;
        context.shadowOffsetX = 0;
        context.shadowOffsetY = 2;

        // 绘制文本
        context.fillStyle = config.textColor;
        context.font = `${config.fontSize}px ${config.fontFamily}`;
        context.textBaseline = 'middle';

        lines.forEach((line, i) => {
            const y = config.padding + (i + 0.5) * lineHeight;
            context.fillText(line, config.padding, y);
        });
    }

    // 显示通知
    function showNotification(message, type = 'info') {
        const notification = document.createElement('div');
        notification.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            padding: 12px 24px;
            background: ${type === 'success' ? '#4caf50' : type === 'warning' ? '#ff9800' : '#f44336'};
            color: white;
            border-radius: 4px;
            font-size: 14px;
            z-index: 9999;
            box-shadow: 0 2px 5px rgba(0,0,0,0.2);
            animation: fadeInOut 3s ease-in-out;
        `;

        notification.textContent = message;
        document.body.appendChild(notification);

        setTimeout(() => {
            notification.remove();
        }, 3000);
    }

    // 主要事件处理函数
    async function handleKeyPress(event) {
        if (!(event.altKey && event.key.toLowerCase() === 'i')) return;

        const selection = getSelectedText();
        if (!selection) {
            showNotification('请先选中文本!', 'warning');
            return;
        }

        try {
            const context = document.createElement('canvas').getContext('2d');
            context.font = `${config.fontSize}px ${config.fontFamily}`;

            const lines = selection.split('\n').flatMap(line =>
                wrapText(context, line, config.maxWidth)
            );

            const { canvas, context: finalContext, lineHeight } = setupCanvas(lines);
            drawImage(canvas, finalContext, lines, lineHeight);

            const blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/png'));
            await navigator.clipboard.write([
                new ClipboardItem({ 'image/png': blob })
            ]);

            showNotification('图片已复制到剪贴板!', 'success');
        } catch (error) {
            console.error('转换失败:', error);
            showNotification('转换失败,请检查权限或浏览器兼容性。', 'error');
        }
    }

    // 注册事件监听器
    document.addEventListener('keydown', handleKeyPress);

    // 添加样式
    const style = document.createElement('style');
    style.textContent = `
        @keyframes fadeInOut {
            0% { opacity: 0; transform: translateY(-20px); }
            10% { opacity: 1; transform: translateY(0); }
            90% { opacity: 1; transform: translateY(0); }
            100% { opacity: 0; transform: translateY(-20px); }
        }
    `;
    document.head.appendChild(style);
})();