Greasy Fork

Greasy Fork is available in English.

猴子都会用的Bangumi文字渐变生成器

在 Bangumi 文本框工具栏中添加渐变生成器

目前为 2024-11-02 提交的版本,查看 最新版本

// ==UserScript==
// @name        猴子都会用的Bangumi文字渐变生成器
// @namespace    https://github.com/wakabayu
// @version      1.3
// @description  在 Bangumi 文本框工具栏中添加渐变生成器
// @include      /^https?:\/\/(bgm\.tv|chii\.in|bangumi\.tv)\/.*/
// @grant        none
// @license      wataame
// @author      wataame
// @homepage    https://bgm.tv/user/wataame
// ==/UserScript==

(function() {
    'use strict';

    // 添加渐变按钮到所有工具栏
    function addGradientButtonToAllToolbars() {
        document.querySelectorAll('.markItUpHeader').forEach(toolbar => {
            if (!toolbar.querySelector('.gradientButton')) {
                const gradientButton = document.createElement('a');
                gradientButton.href = 'javascript:void(0);';
                gradientButton.className = 'markItUpButton gradientButton';
                gradientButton.title = '生成渐变文字';
                gradientButton.innerHTML = '<span style="font-weight: bold; color: #0ebeff;">渐变</span>';
                toolbar.appendChild(gradientButton);

                const textarea = toolbar.closest('.markItUpContainer').querySelector('textarea');
                gradientButton.addEventListener('click', () => {
                    if (!textarea) return alert('找不到对应的文本区域');
                    const selectedText = textarea.value.substring(textarea.selectionStart, textarea.selectionEnd);
                    if (!selectedText) return alert('请先选中需要应用渐变的文字');
                    openColorPicker(selectedText, textarea);
                });
            }
        });
    }

    // 生成颜色选择器
    function openColorPicker(selectedText, textarea) {
        const colorPickerContainer = document.createElement('div');
        colorPickerContainer.style = "position:fixed;top:50%;left:50%;transform:translate(-50%, -50%);background:#fff;padding:20px;border:1px solid #ccc;z-index:9999;box-shadow:0 0 10px rgba(0,0,0,0.1)";
        
        colorPickerContainer.innerHTML = `
            <label>选择起始颜色:<input type="color" value="#0ebeff" id="startColor"></label><br>
            <label>选择结束颜色:<input type="color" value="#5e95e6" id="endColor"></label><br>
            <label>输入步数:<input type="number" min="1" value="${selectedText.length}" id="steps"></label><br>
            <button id="generate">生成</button> <button id="cancel">取消</button><br>
            <div id="historyContainer" style="margin-top: 10px;">
                <strong>最近使用的方案:</strong><br>
            </div>
        `;
        
        document.body.appendChild(colorPickerContainer);
        loadGradientHistory();

        document.querySelector('#generate').onclick = () => {
            const startColor = document.querySelector('#startColor').value;
            const endColor = document.querySelector('#endColor').value;
            const steps = parseInt(document.querySelector('#steps').value);
            if (isNaN(steps) || steps <= 0) return alert('请输入有效的步数');

            const gradientText = generateGradientText(selectedText, startColor, endColor, steps);
            const beforeText = textarea.value.substring(0, textarea.selectionStart);
            const afterText = textarea.value.substring(textarea.selectionEnd);
            textarea.value = beforeText + gradientText + afterText;

            saveGradientHistory(startColor, endColor);
            document.body.removeChild(colorPickerContainer);
        };

        document.querySelector('#cancel').onclick = () => document.body.removeChild(colorPickerContainer);
    }

    // 记录和加载最近的渐变方案
    function saveGradientHistory(startColor, endColor) {
        const history = JSON.parse(localStorage.getItem('gradientHistory') || '[]');
        const newEntry = { start: startColor, end: endColor };

        // 检查是否已存在相同的方案,避免重复添加
        if (!history.some(entry => entry.start === startColor && entry.end === endColor)) {
            history.unshift(newEntry); // 添加新方案到开头
            if (history.length > 5) history.pop(); // 只保留最近 5 条
            localStorage.setItem('gradientHistory', JSON.stringify(history));
        }
    }

    function loadGradientHistory() {
        const historyContainer = document.querySelector('#historyContainer');
        const history = JSON.parse(localStorage.getItem('gradientHistory') || '[]');

        historyContainer.innerHTML = '<strong>最近方案:</strong><br>';
        history.forEach((entry, index) => {
            const historyButton = document.createElement('button');
            historyButton.style = `background: linear-gradient(to right, ${entry.start}, ${entry.end}); border: none; color: #fff; padding: 5px; margin: 2px; cursor: pointer;`;
            historyButton.innerText = `方案 ${index + 1}`;
            historyButton.onclick = () => {
                document.querySelector('#startColor').value = entry.start;
                document.querySelector('#endColor').value = entry.end;
            };
            historyContainer.appendChild(historyButton);
        });
    }

    // 生成渐变文字,按步数分段添加颜色
    function generateGradientText(text, startColor, endColor, steps) {
        const startRGB = hexToRgb(startColor), endRGB = hexToRgb(endColor);
        const segmentLength = Math.ceil(text.length / steps);
        let result = '';

        for (let i = 0; i < steps; i++) {
            const ratio = i / (steps - 1);
            const r = clamp(Math.round(startRGB.r + ratio * (endRGB.r - startRGB.r)));
            const g = clamp(Math.round(startRGB.g + ratio * (endRGB.g - startRGB.g)));
            const b = clamp(Math.round(startRGB.b + ratio * (endRGB.b - startRGB.b)));
            const color = `#${rgbToHex(r)}${rgbToHex(g)}${rgbToHex(b)}`;

            // 获取当前段的文本内容并应用颜色
            const segmentText = text.slice(i * segmentLength, (i + 1) * segmentLength);
            result += `[color=${color}]${segmentText}[/color]`;
        }
        return result;
    }

    // 将值限制在 0-255 范围内
    const clamp = value => Math.max(0, Math.min(255, value));

    // 十六进制转 RGB 和 RGB 转 十六进制
    const hexToRgb = hex => ({ r: parseInt(hex.slice(1, 3), 16), g: parseInt(hex.slice(3, 5), 16), b: parseInt(hex.slice(5, 7), 16) });
    const rgbToHex = value => value.toString(16).padStart(2, '0');

    // 使用 MutationObserver 确保所有工具栏加载后添加按钮
    new MutationObserver(() => addGradientButtonToAllToolbars()).observe(document.body, { childList: true, subtree: true });
})();