(function () { "use strict"; const colorCache = {}; function parseColor(colorStr) { const div = document.createElement("div"); div.style.color = colorStr; document.body.appendChild(div); const computedColor = getComputedStyle(div).color; document.body.removeChild(div); const rgbaMatch = computedColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*[\d.]+)?\)/); if (!rgbaMatch) return [0, 0, 0]; const r = parseInt(rgbaMatch[1], 10); const g = parseInt(rgbaMatch[2], 10); const b = parseInt(rgbaMatch[3], 10); return [r, g, b]; } function rgbToHsl(r, g, b) { r /= 255; g /= 255; b /= 255; const max = Math.max(r, g, b), min = Math.min(r, g, b), delta = max - min; let h = 0, s = max === 0 ? 0 : delta / (max + (max + min < 1 ? delta : 2 - delta)), l = (max + min) / 2; switch (max) { case r: h = (g - b) / delta + (g < b ? 6 : 0); break; case g: h = (b - r) / delta + 2; break; case b: h = (r - g) / delta + 4; break; } h = Math.round(h * 60); s = Math.round(s * 100); l = Math.round(l * 100); return [h, s, l]; } function getRelativeLuminance(rgb) { const sRGB = [...rgb].map(v => { v /= 255; return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4); }); return 0.2126 * sRGB[0] + 0.7152 * sRGB[1] + 0.0722 * sRGB[2]; } function getContrastRatio(rgb1, rgb2) { const lum1 = getRelativeLuminance(rgb1); const lum2 = getRelativeLuminance(rgb2); return lum1 > lum2 ? (lum1 + 0.05) / (lum2 + 0.05) : (lum2 + 0.05) / (lum1 + 0.05); } function getBgColor(element) { let currentElement = element; while (currentElement) { const bg = getComputedStyle(currentElement).backgroundColor; if (bg && bg !== "rgba(0, 0, 0, 0)" && bg !== "transparent") { return parseColor(bg); } currentElement = currentElement.parentElement; } return parseColor("white"); } function getColorValues(el) { const fg = getComputedStyle(el).color; return { foregroundColor: parseColor(fg), backgroundColor: getBgColor(el) }; } const SELECTOR = "font[color]"; const THRESHOLD = 4.5; const LIGHTEN = 20; const DESATURATE = 20; const elements = document.querySelectorAll(SELECTOR); let index = 0; function processBatch() { const batchSize = 20; for (let i = 0; i < batchSize && index < elements.length; i++, index++) { const el = elements[index]; if (el.dataset.colorAdjusted === "true") continue; const {foregroundColor, backgroundColor} = getColorValues(el); const contrastRatio = getContrastRatio(foregroundColor, backgroundColor); if (contrastRatio < THRESHOLD) { const [r, g, b] = foregroundColor; let hsl = rgbToHsl(r, g, b); const bgLuminance = getRelativeLuminance(backgroundColor); const adjustmentFactor = (THRESHOLD - contrastRatio) / THRESHOLD; // Уменьшаем насыщенность всегда hsl[1] = Math.max(0, hsl[1] - DESATURATE * adjustmentFactor); // Только для тёмного фона — повышаем яркость if (bgLuminance <= 0.5) { let brightnessIncrease = 0; if (contrastRatio < 0.7 * THRESHOLD) { brightnessIncrease = 3 * LIGHTEN; } else if (contrastRatio < THRESHOLD) { brightnessIncrease = 0.5 * LIGHTEN; } hsl[2] = Math.min(100, hsl[2] + brightnessIncrease * adjustmentFactor); } el.style.color = `hsl(${hsl[0]}, ${hsl[1]}%, ${hsl[2]}%)`; el.dataset.colorAdjusted = "true"; } } if (index < elements.length) { setTimeout(processBatch, 0); } } console.time("Adjust colors"); processBatch(); console.timeEnd("Adjust colors"); })(); |