Greasy Fork

Greasy Fork is available in English.

 全局字体

全局修改字体

当前为 2025-12-07 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name  全局字体
// @author gemini
// @version  1.02
// @match        *://*/*
// @run-at document-start
// @grant  GM_addStyle
// @description 全局修改字体
// @namespace http://greasyfork.icu/users/22620
// ==/UserScript==

(function() {
    'use strict';
    // --- 配置项 ---
    const MY_NEW_FONT = '"MiSans"';
    // 常见的图标字体、特殊符号、特殊元素选择器数组
    // 脚本只会对 不匹配 这些选择器 的元素应用新字体
    const ICON_EXCLUSION_SELECTORS_ARRAY = [
        'i',
        '[class*="ico"]',
        '[class*="fa"]'
    ];
    // 通过 `:not()` 组合,生成最终的排除选择器。
    // 例如:':not(i):not([class*="icon"])'
    const ICON_EXCLUSION_SELECTOR_STRING = ICON_EXCLUSION_SELECTORS_ARRAY
        .map(selector => `:not(${selector})`)
        .join('');

    // --- 内部变量 (不暴露到全局) ---
    let phaseOneStyleElement = null;

    /**
     * 根据字体名称和排除选择器生成完整的 CSS 规则。
     * @param {string} fontFamily - 要应用的 font-family 字符串。
     * @returns {string} 完整的 CSS 内容。
     */
    const generateCSS = (fontFamily) => {
        return `
            /* 全局选择器,排除图标和特殊符号元素 */
            *${ICON_EXCLUSION_SELECTOR_STRING} {
                font-family: ${fontFamily} !important;
            }
        `;
    };

    /**
     * 阶段一:在 document-start 立即注入初始 CSS 以防止闪烁 (FOUC)。
     * @param {string} newFontFamily - 要使用的主要字体。
     */
    const injectPhaseOneCSS = (newFontFamily) => {
        const cssContent = generateCSS(newFontFamily);
        phaseOneStyleElement = GM_addStyle(cssContent);
        console.log(`⏱️ 阶段一:立即注入初始字体: ${newFontFamily}`);
    };

    /**
     * 阶段二:等待所有字体加载完成,识别并保护页面原有字体(特别是图标字体),然后更新 CSS。
     * @param {string} newFontFamily - 要使用的主要字体。
     */
    const runPhaseTwo = async (newFontFamily) => {
        try {
            // 等待所有 @font-face 规则加载完成
            await document.fonts.ready;
            const protectedFontNames = new Set();
            // 遍历所有已加载的 FontFace,收集其 family 名称
            document.fonts.forEach(fontFace => {
                protectedFontNames.add(`"${fontFace.family}"`);
            });

            // 将 Set 转换为数组并用逗号连接,形成字体栈
            const protectedFontFamily = Array.from(protectedFontNames).join(', ');
            let finalFontFamily;
            
            if (protectedFontFamily) {
                finalFontFamily = `${newFontFamily}, ${protectedFontFamily}`;
            } else {
                // 如果没有识别到其他字体
                finalFontFamily = `${newFontFamily}`;
            }
            
            const cssContentFinal = generateCSS(finalFontFamily);
            // 更新阶段一注入的 <style> 元素的内容,实现平滑过渡
            if (phaseOneStyleElement && typeof phaseOneStyleElement.textContent !== 'undefined') {
                phaseOneStyleElement.textContent = cssContentFinal;
                console.log("✅ 阶段二:全局字体已最终更新并保护原有字体:", finalFontFamily);
            } else {
                 // 备用:如果 GM_addStyle 返回的对象无法直接修改 textContent,则再次注入
                GM_addStyle(cssContentFinal);
                 console.log("✅ 阶段二:全局字体已最终更新并保护原有字体 (通过再次注入):", finalFontFamily);
            }

        } catch (error) {
            console.error("⚠️ 字体加载或识别过程中出错,将保持初始字体设置:", error);
        }
    };

    // --- 脚本主执行流程 ---
    injectPhaseOneCSS(MY_NEW_FONT);
    runPhaseTwo(MY_NEW_FONT);
    
})();