Advanced background protection with full gradient support
Versão de:
// ==UserScript==
// @name Page Background Controller
// @namespace Background Color
// @description Advanced background protection with full gradient support
// @version 3.1
// @include http*
// @include ftp
// @match *://*/*
// @exclude
// @license MIT
// @grant GM_addStyle
// ==/UserScript==
/**
* 渐变感知背景控制器
*
* 新增功能:
* - 完整的CSS渐变支持(linear、radial、conic等)
* - 智能渐变颜色替换
* - 保持渐变方向和样式
* - 渐变缓存优化
*/
(function() {
'use strict';
// 配置
const CONFIG = {
// Solarized护眼色彩
colors: {
primary: '#FDF6E3', // Solarized base3 (最浅)
secondary: '#EEE8D5', // Solarized base2 (浅)
tertiary: '#E8DCC6', // 介于base2和base3之间
accent: '#DDD6C1', // 更深一点的米色
text: '#073642', // Solarized base02 (深)
link: '#268bd2' // Solarized blue
},
// 渐变配置
gradient: {
preserveDirection: true, // 保持渐变方向
minColorStops: 2, // 最少颜色停止点
maxColorStops: 5, // 最多颜色停止点
blendIntensity: 0.8, // 混合强度
smoothTransition: true // 平滑过渡
},
// 检测阈值
thresholds: {
lightness: 230, // 亮度阈值
saturation: 30, // 饱和度阈值
gradientComplexity: 3 // 渐变复杂度阈值
}
};
// 颜色工具类
class ColorUtils {
// 解析各种颜色格式
static parseColor(colorStr) {
if (!colorStr) return null;
// RGB/RGBA
let match = colorStr.match(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+))?\s*\)/);
if (match) {
return {
r: parseInt(match[1]),
g: parseInt(match[2]),
b: parseInt(match[3]),
a: match[4] ? parseFloat(match[4]) : 1,
format: 'rgb'
};
}
// HSL/HSLA
match = colorStr.match(/hsla?\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*(?:,\s*([\d.]+))?\s*\)/);
if (match) {
const hsl = {
h: parseInt(match[1]),
s: parseInt(match[2]),
l: parseInt(match[3]),
a: match[4] ? parseFloat(match[4]) : 1,
format: 'hsl'
};
return this.hslToRgb(hsl);
}
// HEX
match = colorStr.match(/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i);
if (match) {
return {
r: parseInt(match[1], 16),
g: parseInt(match[2], 16),
b: parseInt(match[3], 16),
a: 1,
format: 'hex'
};
}
// 颜色名称
const namedColors = {
'white': { r: 255, g: 255, b: 255, a: 1 },
'black': { r: 0, g: 0, b: 0, a: 1 },
'red': { r: 255, g: 0, b: 0, a: 1 },
'green': { r: 0, g: 128, b: 0, a: 1 },
'blue': { r: 0, g: 0, b: 255, a: 1 },
'yellow': { r: 255, g: 255, b: 0, a: 1 },
'cyan': { r: 0, g: 255, b: 255, a: 1 },
'magenta': { r: 255, g: 0, b: 255, a: 1 },
'silver': { r: 192, g: 192, b: 192, a: 1 },
'gray': { r: 128, g: 128, b: 128, a: 1 },
'transparent': { r: 0, g: 0, b: 0, a: 0 }
};
if (namedColors[colorStr.toLowerCase()]) {
return { ...namedColors[colorStr.toLowerCase()], format: 'named' };
}
return null;
}
// HSL转RGB
static hslToRgb({ h, s, l, a }) {
h /= 360;
s /= 100;
l /= 100;
let r, g, b;
if (s === 0) {
r = g = b = l;
} else {
const hue2rgb = (p, q, t) => {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1/6) return p + (q - p) * 6 * t;
if (t < 1/2) return q;
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
};
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return {
r: Math.round(r * 255),
g: Math.round(g * 255),
b: Math.round(b * 255),
a: a,
format: 'rgb'
};
}
// 判断是否为亮色
static isLightColor(color) {
if (!color) return false;
const { r, g, b } = color;
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
return brightness > CONFIG.thresholds.lightness;
}
// 颜色混合
static blendColors(color1, color2, ratio = 0.5) {
if (!color1 || !color2) return color1 || color2;
return {
r: Math.round(color1.r * (1 - ratio) + color2.r * ratio),
g: Math.round(color1.g * (1 - ratio) + color2.g * ratio),
b: Math.round(color1.b * (1 - ratio) + color2.b * ratio),
a: color1.a * (1 - ratio) + color2.a * ratio,
format: 'rgb'
};
}
// 颜色到字符串
static colorToString(color) {
if (!color) return 'transparent';
const { r, g, b, a } = color;
if (a === 1) {
return `rgb(${r}, ${g}, ${b})`;
} else {
return `rgba(${r}, ${g}, ${b}, ${a})`;
}
}
// 智能替换亮色
static replaceIfLight(color) {
if (!this.isLightColor(color)) {
return color; // 保持深色
}
const { r, g, b, a } = color;
const brightness = (r + g + b) / 3;
// 根据亮度选择替换色
let replacement;
if (brightness > 250) {
replacement = this.parseColor(CONFIG.colors.primary);
} else if (brightness > 240) {
replacement = this.parseColor(CONFIG.colors.secondary);
} else if (brightness > 220) {
replacement = this.parseColor(CONFIG.colors.tertiary);
} else {
replacement = this.parseColor(CONFIG.colors.accent);
}
// 保持透明度
if (replacement) {
replacement.a = a;
}
return replacement;
}
}
// 渐变解析器
class GradientParser {
// 解析CSS渐变
static parseGradient(backgroundImage) {
if (!backgroundImage || backgroundImage === 'none') return null;
const gradientTypes = [
'linear-gradient',
'radial-gradient',
'conic-gradient',
'repeating-linear-gradient',
'repeating-radial-gradient'
];
for (const type of gradientTypes) {
const regex = new RegExp(`${type}\\s*\\(([^)]+)\\)`, 'i');
const match = backgroundImage.match(regex);
if (match) {
return {
type: type,
rawParams: match[1],
fullMatch: match[0],
parsed: this.parseGradientParams(type, match[1])
};
}
}
return null;
}
// 解析渐变参数
static parseGradientParams(type, params) {
const result = {
direction: null,
colorStops: [],
shape: null,
size: null,
position: null
};
// 分割参数(处理嵌套函数)
const parts = this.splitParams(params);
if (type === 'linear-gradient' || type === 'repeating-linear-gradient') {
result.direction = this.parseDirection(parts[0]);
result.colorStops = this.parseColorStops(parts.slice(result.direction ? 1 : 0));
} else if (type === 'radial-gradient' || type === 'repeating-radial-gradient') {
const { shape, size, position, remaining } = this.parseRadialParams(parts);
result.shape = shape;
result.size = size;
result.position = position;
result.colorStops = this.parseColorStops(remaining);
} else if (type === 'conic-gradient') {
const { angle, position, remaining } = this.parseConicParams(parts);
result.direction = angle;
result.position = position;
result.colorStops = this.parseColorStops(remaining);
}
return result;
}
// 分割参数(处理嵌套)
static splitParams(params) {
const parts = [];
let current = '';
let depth = 0;
let inString = false;
let stringChar = '';
for (let i = 0; i < params.length; i++) {
const char = params[i];
if ((char === '"' || char === "'") && !inString) {
inString = true;
stringChar = char;
} else if (char === stringChar && inString) {
inString = false;
stringChar = '';
} else if (!inString) {
if (char === '(') depth++;
else if (char === ')') depth--;
else if (char === ',' && depth === 0) {
parts.push(current.trim());
current = '';
continue;
}
}
current += char;
}
if (current.trim()) {
parts.push(current.trim());
}
return parts;
}
// 解析线性渐变方向
static parseDirection(part) {
if (!part) return null;
const trimmed = part.trim().toLowerCase();
// 角度
if (trimmed.match(/^\d+deg$/)) {
return trimmed;
}
// 关键字方向
const directions = [
'to top', 'to bottom', 'to left', 'to right',
'to top left', 'to top right', 'to bottom left', 'to bottom right'
];
if (directions.includes(trimmed)) {
return trimmed;
}
return null;
}
// 解析颜色停止点
static parseColorStops(parts) {
const colorStops = [];
for (const part of parts) {
const stop = this.parseColorStop(part);
if (stop) {
colorStops.push(stop);
}
}
return colorStops;
}
// 解析单个颜色停止点
static parseColorStop(part) {
const trimmed = part.trim();
// 匹配颜色+位置的模式
// 例如: "red 0%", "#fff 50px", "rgb(255,0,0) 25%"
const match = trimmed.match(/^(.+?)(?:\s+([\d.]+%|[\d.]+px|[\d.]+))?$/);
if (match) {
const color = ColorUtils.parseColor(match[1].trim());
const position = match[2] || null;
if (color) {
return {
color: color,
position: position
};
}
}
return null;
}
// 解析径向渐变参数
static parseRadialParams(parts) {
let shape = null;
let size = null;
let position = null;
let remaining = [...parts];
// 简化实现,主要处理颜色停止点
// 在实际使用中可以扩展更复杂的径向渐变解析
return { shape, size, position, remaining };
}
// 解析圆锥渐变参数
static parseConicParams(parts) {
let angle = null;
let position = null;
let remaining = [...parts];
// 简化实现,主要处理颜色停止点
// 在实际使用中可以扩展更复杂的圆锥渐变解析
return { angle, position, remaining };
}
}
// 渐变转换器
class GradientTransformer {
// 转换渐变为护眼版本
static transformGradient(gradientInfo) {
if (!gradientInfo || !gradientInfo.parsed) return null;
const { type, parsed } = gradientInfo;
const { direction, colorStops, shape, size, position } = parsed;
// 转换颜色停止点
const transformedStops = this.transformColorStops(colorStops);
if (transformedStops.length === 0) return null;
// 重构渐变字符串
return this.buildGradientString(type, {
direction,
colorStops: transformedStops,
shape,
size,
position
});
}
// 转换颜色停止点
static transformColorStops(colorStops) {
if (!colorStops || colorStops.length === 0) return [];
const transformed = [];
for (const stop of colorStops) {
if (stop && stop.color) {
const newColor = ColorUtils.replaceIfLight(stop.color);
if (newColor) {
transformed.push({
color: newColor,
position: stop.position
});
}
}
}
// 确保至少有2个颜色停止点
if (transformed.length === 1) {
const single = transformed[0];
transformed.push({
color: single.color,
position: '100%'
});
} else if (transformed.length === 0) {
// 如果没有有效的颜色,使用默认护眼色
const defaultColor = ColorUtils.parseColor(CONFIG.colors.primary);
transformed.push(
{ color: defaultColor, position: '0%' },
{ color: defaultColor, position: '100%' }
);
}
return transformed;
}
// 构建渐变字符串
static buildGradientString(type, { direction, colorStops, shape, size, position }) {
let params = [];
if (type === 'linear-gradient' || type === 'repeating-linear-gradient') {
if (direction) params.push(direction);
} else if (type === 'radial-gradient' || type === 'repeating-radial-gradient') {
let radialParams = [];
if (shape) radialParams.push(shape);
if (size) radialParams.push(size);
if (position) radialParams.push(`at ${position}`);
if (radialParams.length > 0) {
params.push(radialParams.join(' '));
}
} else if (type === 'conic-gradient') {
if (direction) params.push(`from ${direction}`);
if (position) params.push(`at ${position}`);
}
// 添加颜色停止点
const stopStrings = colorStops.map(stop => {
const colorStr = ColorUtils.colorToString(stop.color);
return stop.position ? `${colorStr} ${stop.position}` : colorStr;
});
params = params.concat(stopStrings);
return `${type}(${params.join(', ')})`;
}
}
// 主处理器
class BackgroundProcessor {
constructor() {
this.cache = new Map();
this.processedElements = new WeakSet();
}
// 处理元素背景
processElement(element) {
if (this.processedElements.has(element)) return;
try {
const computedStyle = getComputedStyle(element);
const backgroundImage = computedStyle.backgroundImage;
const backgroundColor = computedStyle.backgroundColor;
let hasChanges = false;
// 处理渐变背景
if (backgroundImage && backgroundImage !== 'none') {
const newBackgroundImage = this.processBackgroundImage(backgroundImage);
if (newBackgroundImage !== backgroundImage) {
element.style.setProperty('background-image', newBackgroundImage, 'important');
hasChanges = true;
}
}
// 处理纯色背景
if (backgroundColor && backgroundColor !== 'rgba(0, 0, 0, 0)') {
const color = ColorUtils.parseColor(backgroundColor);
const newColor = ColorUtils.replaceIfLight(color);
if (newColor && newColor !== color) {
const newColorStr = ColorUtils.colorToString(newColor);
element.style.setProperty('background-color', newColorStr, 'important');
hasChanges = true;
}
}
// 处理文本颜色
const textColor = computedStyle.color;
if (textColor) {
const color = ColorUtils.parseColor(textColor);
if (ColorUtils.isLightColor(color)) {
element.style.setProperty('color', CONFIG.colors.text, 'important');
hasChanges = true;
}
}
if (hasChanges) {
this.processedElements.add(element);
}
} catch (error) {
console.warn('Error processing element background:', error);
}
}
// 处理背景图像(渐变)
processBackgroundImage(backgroundImage) {
// 检查缓存
if (this.cache.has(backgroundImage)) {
return this.cache.get(backgroundImage);
}
// 分割多个背景图像
const backgrounds = this.splitBackgrounds(backgroundImage);
const processedBackgrounds = [];
for (const bg of backgrounds) {
const gradientInfo = GradientParser.parseGradient(bg.trim());
if (gradientInfo) {
const transformed = GradientTransformer.transformGradient(gradientInfo);
processedBackgrounds.push(transformed || bg);
} else {
processedBackgrounds.push(bg); // 非渐变背景保持原样
}
}
const result = processedBackgrounds.join(', ');
// 缓存结果
this.cache.set(backgroundImage, result);
return result;
}
// 分割多个背景
splitBackgrounds(backgroundImage) {
// 简化实现:按逗号分割,但需要注意函数内的逗号
const backgrounds = [];
let current = '';
let depth = 0;
for (let i = 0; i < backgroundImage.length; i++) {
const char = backgroundImage[i];
if (char === '(') {
depth++;
} else if (char === ')') {
depth--;
} else if (char === ',' && depth === 0) {
backgrounds.push(current.trim());
current = '';
continue;
}
current += char;
}
if (current.trim()) {
backgrounds.push(current.trim());
}
return backgrounds;
}
// 批量处理元素
processBatch(elements, startIndex = 0) {
const batchSize = 50;
const endIndex = Math.min(startIndex + batchSize, elements.length);
for (let i = startIndex; i < endIndex; i++) {
this.processElement(elements[i]);
}
if (endIndex < elements.length) {
requestAnimationFrame(() => {
this.processBatch(elements, endIndex);
});
}
}
// 清理缓存
cleanup() {
if (this.cache.size > 1000) {
this.cache.clear();
}
}
}
// CSS样式注入器
const cssInjector = {
injectBaseStyles() {
const css = `
/* 基础护眼样式 */
html, body {
background-color: ${CONFIG.colors.primary} !important;
color: ${CONFIG.colors.text} !important;
}
/* 常见渐变覆盖 */
.gradient, .bg-gradient, [class*="gradient"] {
background-image: linear-gradient(to bottom, ${CONFIG.colors.primary}, ${CONFIG.colors.secondary}) !important;
}
/* 白色渐变覆盖 */
[style*="linear-gradient"][style*="white"],
[style*="linear-gradient"][style*="#fff"],
[style*="linear-gradient"][style*="rgb(255"] {
background-image: linear-gradient(135deg, ${CONFIG.colors.primary} 0%, ${CONFIG.colors.secondary} 50%, ${CONFIG.colors.tertiary} 100%) !important;
}
/* 径向渐变覆盖 */
[style*="radial-gradient"][style*="white"],
[style*="radial-gradient"][style*="#fff"] {
background-image: radial-gradient(circle, ${CONFIG.colors.secondary}, ${CONFIG.colors.primary}) !important;
}
`;
try {
GM_addStyle(css);
} catch (e) {
const style = document.createElement('style');
style.textContent = css;
(document.head || document.documentElement).appendChild(style);
}
}
};
// 主控制器
function init() {
console.log('🎨 Gradient-Aware Background Controller starting...');
const processor = new BackgroundProcessor();
// 1. 注入基础CSS
cssInjector.injectBaseStyles();
// 2. 处理现有元素
requestAnimationFrame(() => {
const elements = document.querySelectorAll('*');
processor.processBatch(Array.from(elements));
});
// 3. 监听DOM变化
const observer = new MutationObserver((mutations) => {
const newElements = [];
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE) {
newElements.push(node);
const children = node.querySelectorAll('*');
newElements.push(...children);
}
});
});
if (newElements.length > 0) {
processor.processBatch(newElements);
}
});
observer.observe(document, {
childList: true,
subtree: true,
attributes: false
});
// 4. 定期清理缓存
setInterval(() => {
processor.cleanup();
}, 60000);
console.log('✅ Gradient-Aware Background Controller activated');
}
// 启动
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();