// ==UserScript== // @name  全局字体 // @author gemini // @version  1.02 // @match        *://*/* // @run-at document-start // @grant  GM_addStyle // @description 全局修改字体 // @namespace https://greasyfork.org/users/22620 // @downloadURL none // ==/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); // 更新阶段一注入的