Greasy Fork

Greasy Fork is available in English.

护眼脚本

修改网页背景色,让网页背景色偏白的部分变成乡土黄、豆沙绿等护眼色。优化了性能,解决了动态加载页面的卡顿问题,支持透明背景保留。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name        护眼脚本
// @namespace   https://zecdn.top
// @description 修改网页背景色,让网页背景色偏白的部分变成乡土黄、豆沙绿等护眼色。优化了性能,解决了动态加载页面的卡顿问题,支持透明背景保留。
// @include     http*
// @include     ftp*
// @version     1.5
// @grant       GM_registerMenuCommand
// @grant       GM_setValue
// @grant       GM_getValue
// ==/UserScript==
 
(function() {
    'use strict';
 
    const CONFIG = {
        threshold: 242,
        colors: {
            yellow: { name: "乡土黄", hex: "#F6F4EC" },
            green:  { name: "豆沙绿", hex: "#CCE8CF" },
            grey:   { name: "浅色灰", hex: "#F2F2F2" },
            olive:  { name: "淡橄榄", hex: "#E1E6D7" }
        },
        defaultColorKey: "yellow"
    };
 
    let selectedKey = GM_getValue("colorValue", CONFIG.defaultColorKey);
    let currentColorHex = CONFIG.colors[selectedKey].hex;
 
    // --- 核心优化 1: 使用 CSS 注入代替逐个修改 style 属性 ---
    // 这样不需要在 MutationObserver 里频繁操作 DOM 属性,减少重排
    const styleId = 'eye-protection-style';
    function updateGlobalStyle() {
        let styleTag = document.getElementById(styleId);
        if (!styleTag) {
            styleTag = document.createElement('style');
            styleTag.id = styleId;
            document.documentElement.appendChild(styleTag);
        }
        // 这里的逻辑:只针对标记了 data-eye-protected 的元素生效
        styleTag.innerHTML = `[data-eye-protected="true"] { background-color: ${currentColorHex} !important; }`;
    }
 
    function isLightColor(colorStr) {
        if (!colorStr || colorStr === 'transparent' || colorStr.includes('rgba(0, 0, 0, 0)')) return false;
        const rgb = colorStr.match(/\d+/g);
        if (!rgb || rgb.length < 3) return false;
        // 只要 R, G, B 都大于阈值
        return rgb[0] > CONFIG.threshold && rgb[1] > CONFIG.threshold && rgb[2] > CONFIG.threshold;
    }
 
    // --- 核心优化 2: 精简处理逻辑 ---
    function processElement(el) {
        if (el.nodeType !== 1) return;
        // 已经处理过的跳过
        if (el.dataset.eyeProtected) return;
 
        const bg = window.getComputedStyle(el).backgroundColor;
        if (isLightColor(bg)) {
            el.setAttribute('data-eye-protected', 'true');
        }
    }
 
    // --- 核心优化 3: 带有防抖和任务分解的观察器 ---
    let timer = null;
    const observer = new MutationObserver((mutations) => {
        if (timer) clearTimeout(timer);
        timer = setTimeout(() => {
            // 每次只处理新增的顶层节点,不进行深度递归扫描
            // 深度扫描交给 requestIdleCallback 在浏览器空闲时做
            requestIdleCallback(() => {
                mutations.forEach(m => {
                    m.addedNodes.forEach(node => {
                        if (node.nodeType === 1) {
                            processElement(node);
                            // 只扫描一层子节点,避免知乎这种深层 DOM 导致的卡死
                            const children = node.children;
                            for(let i=0; i<children.length; i++) processElement(children[i]);
                        }
                    });
                });
            });
        }, 100); // 100ms 防抖
    });
 
    function init() {
        updateGlobalStyle();
        // 初次全量扫描(分片处理)
        const all = document.querySelectorAll('body, body *');
        // 使用 requestIdleCallback 避免阻塞首屏渲染
        window.requestIdleCallback(() => {
            all.forEach(el => processElement(el));
        });
 
        observer.observe(document.body, { childList: true, subtree: true });
    }
 
    // 注册菜单
    for (const [key, val] of Object.entries(CONFIG.colors)) {
        GM_registerMenuCommand(val.name, () => {
            GM_setValue("colorValue", key);
            location.reload(); // 切换颜色建议刷新,性能最稳
        });
    }
 
    // 确保 body 存在后再运行
    if (document.body) {
        init();
    } else {
        const observerBody = new MutationObserver(() => {
            if (document.body) {
                observerBody.disconnect();
                init();
            }
        });
        observerBody.observe(document.documentElement, { childList: true });
    }
})();