Web编程技术营地
研究、演示、创新

Color Utils

撰写时间:2025-12-06

修订时间:2025-12-26

GetRandomSoftRGB

GetRandomSoftRGB方法返回一个可选择颜色柔和度的、且使用rgb()形式来表示的字符串。

const { ColorUtils } = await import('/js/esm/ColorUtils.js'); let div = pc.appendHTMLStr("
")[0]; div.style.width = "100px"; div.style.aspectRatio = "1 / 1"; pc.logStatic('rgbStr', ''); let button = document.createElement('button'); button.textContent = 'GetRandomSoftRGB'; pc.appendChild(button); button.onclick = (evt) => { let offset = 50; let rgbStr = ColorUtils.GetRandomSoftRGB(offset); div.style.backgroundColor = rgbStr; pc.logStatic('rgbStr', rgbStr); }; button.dispatchEvent(new MouseEvent('click'));

参数offset是距离RGB通道值域中间值[127, 128]的偏移值。该值越大,则RGB通道的取值范围越广,通道变化就越大,颜色的值域就越大,颜色明暗变化就越大。默认值为30

offset的值域应为[0, 127],这样才能得出[0, 255]的值域。但GetRandomSoftRGB在内部自动进行钳制,因此offset的值可为任意整数。

GetRandomLightRGB

GetRandomLightRGB方法返回一个颜色较亮的、且使用hsl()形式来表示的字符串。

const { ColorUtils } = await import('/js/esm/ColorUtils.js'); let div = pc.appendHTMLStr("
")[0]; div.style.width = "100px"; div.style.aspectRatio = "1 / 1"; pc.logStatic('rgbStr', ''); let button = document.createElement('button'); button.textContent = 'GetRandomLightRGB'; pc.appendChild(button); button.onclick = (evt) => { let rgbStr = ColorUtils.GetRandomLightRGB(); div.style.backgroundColor = rgbStr; pc.logStatic('rgbStr', rgbStr); }; button.dispatchEvent(new MouseEvent('click'));

色相为[0, 359]范围内的随机值,饱和度为50%,亮度为90%

HexToDecimal

HexToDecimal方法将一个使用十六进制#3366AA来表示颜色值的字符串,转换为一个代表RGB通道值的数组。

await import('/js/esm/web-widgets/hex-input/HexInput.js'); const { ColorUtils } = await import('/js/esm/ColorUtils.js'); let div = pc.appendHTMLStr(`
`)[3]; window.addEventListener('hex-input-finish', (evt) => { let hexStr = `#${evt.detail.hex}`; div.style.backgroundColor = hexStr; pc.logStatic('hexStr', hexStr); const [r, g, b] = ColorUtils.HexToDecimal(hexStr); let rgbStr = `rgb(${r}, ${g}, ${b})`; pc.logStatic('rgbStr', rgbStr); });

HslToClampedRgb

HslToClampedRgb方法的3个参数分别代表色度([0, 359])、饱和度([0, 100])及亮度([0, 100]),返回为一个代表RGB通道值且值域均为[0, 1]范围内的数组。

const { ColorUtils } = await import('/js/esm/ColorUtils.js'); let rgbArr = ColorUtils.HslToClampedRgb(125, 30, 50); pc.log(rgbArr);

HslToRgb

HexToDecimal方法的3个参数分别代表色度([0, 359])、饱和度([0, 100])及亮度([0, 100]),返回为一个代表RGB通道值且值域均为[0, 255]范围内的数组。

const { ColorUtils } = await import('/js/esm/ColorUtils.js'); let rgbArr = ColorUtils.HslToRgb(125, 30, 50); pc.log(rgbArr);

ClampedRgbToHsl

ClampedRgbToHsl方法的3个参数分别代表位域为[0, 1]RGB通道值,返回为一个代表HSL颜色值各成份的数组。

const { ColorUtils } = await import('/js/esm/ColorUtils.js'); let hslArr = ColorUtils.ClampedRgbToHsl(0.3, 0.5, 0.9); pc.log(hslArr);

RgbToHsl

RgbToHsl方法的3个参数分别代表位域为[0, 255]RGB通道值,返回为一个代表HSL颜色值各成份的数组。

const { ColorUtils } = await import('/js/esm/ColorUtils.js'); let hslArr = ColorUtils.RgbToHsl(38, 125, 230); pc.log(hslArr);

获取颜色名称

const { ColorUtils } = await import('/js/esm/ColorUtils.js'); let colorNames = ColorUtils.GetColorNames(); pc.log(colorNames);

WCAGColorContrast

rgbToHex

const { WCAGColorContrast } = await import('/js/esm/ColorUtils.js'); const { styleTags } = await import('/js/esm/styleTags.js'); const wcagCC = new WCAGColorContrast(); const renderRGB = (r, g, b) => { const hexColor = wcagCC.rgbToHex(r, g, b); pc.log(hexColor); pc.appendChild(styleTags.div` width: 80px; height: 80px; background-color: ${hexColor}; `); }; renderRGB(125, 33, 22); renderRGB(255, 0, 255);

hexToRgb

const { WCAGColorContrast } = await import('/js/esm/ColorUtils.js'); const { styleTags } = await import('/js/esm/styleTags.js'); const wcagCC = new WCAGColorContrast(); const renderHex = (hexStr) => { const rgbColor = wcagCC.hexToRgb(hexStr); pc.log(rgbColor); pc.appendChild(styleTags.div` width: 80px; height: 80px; background-color: ${hexStr}; `); }; renderHex('#336699'); renderHex('#D6C');

caclLuminance

const { WCAGColorContrast } = await import('/js/esm/ColorUtils.js'); const { styleTags } = await import('/js/esm/styleTags.js'); const wcagCC = new WCAGColorContrast(); const renderLuminance = (r, g, b) => { const lum = wcagCC.caclLuminance(r, g, b); pc.log(lum); const hexColor = wcagCC.rgbToHex(r, g, b); pc.appendChild(styleTags.div` width: 80px; height: 80px; background-color: ${hexColor}; `); }; renderLuminance(125, 33, 22); renderLuminance(88, 125, 215);

getContrastRatio

const { WCAGColorContrast } = await import('/js/esm/ColorUtils.js'); const { styleTags, pStyleTags } = await import('/js/esm/styleTags.js'); const wcagCC = new WCAGColorContrast(); let fgColor = [172, 183, 196]; let bgColor = [43, 43, 43]; const fgLum = wcagCC.caclLuminance(...fgColor); const bgLum = wcagCC.caclLuminance(...bgColor); const contrastRatio = wcagCC.getContrastRatio(fgLum, bgLum); pc.log("foreground luminance: %f", fgLum); pc.log("background luminance: %f", bgLum); pc.log("contrast ratio: %f", contrastRatio); visualizeContrastRatio(); function visualizeContrastRatio() { let div = pc.appendChild(styleTags.div` background-color: ${wcagCC.rgbToHex(...bgColor)}; width: 180px; aspect-ratio: 16 / 9; border: 1px solid gray; display: grid; place-items: center; `); pStyleTags.p` color: ${wcagCC.rgbToHex(...fgColor)}; font-size: 1.5em; `.then(p => { p.textContent = "Some text"; div.appendChild(p); }); };

meetsWCAG

const { WCAGColorContrast } = await import('/js/esm/ColorUtils.js'); const { styleTags, pStyleTags } = await import('/js/esm/styleTags.js'); const wcagCC = new WCAGColorContrast(); let fgColor = "#ACB7C4"; let bgColor = "#2B2B2B"; const result1 = wcagCC.meetsWCAG(fgColor, bgColor, "AA"); pc.log(result1); const result2 = wcagCC.meetsWCAG(fgColor, bgColor, "AAA"); pc.log(result2); visualizeContrastRatio(); function visualizeContrastRatio() { let div = pc.appendChild(styleTags.div` background-color: ${bgColor}; width: 180px; aspect-ratio: 16 / 9; border: 1px solid gray; display: grid; place-items: center; `); pStyleTags.p` color: ${fgColor}; font-size: 1.5em; `.then(p => { p.textContent = "Some text"; div.appendChild(p); }); };

getAccessibleForegrounds

getAccessibleForegrounds方法将一个指定的背景颜色,按所指定的AA级或AAA级的标准,分别与147个有颜色名称的颜色值相比较,最终筛选出高对比度的一组颜色。

const { WCAGColorContrast } = await import('/js/esm/ColorUtils.js'); const { styleTags, pStyleTags } = await import('/js/esm/styleTags.js'); await import('/js/esm/web-widgets/hex-input/HexInput.js'); const COLOR_BLOCK_SIZE = '140px'; const wcagCC = new WCAGColorContrast(); pc.appendHTMLStr(` `); pc.logStatic('total', 'Total count: %d', 0); let container = pc.appendChild(styleTags.div` display: grid; grid-template-columns: repeat(auto-fit, minmax(${COLOR_BLOCK_SIZE}, 1fr)); gap: 0.5em; place-items: center; `); addEventListener('hex-input-finish', (evt) => { container.innerHTML = ``; let bgColor = `#${evt.detail.hex}`; let candidates = wcagCC.getAccessibleForegrounds(bgColor, { level: "AAA" }); pc.logStatic('total', 'Total count: %d', candidates.length); candidates.forEach(candidate => { pStyleTags.div` background-color: ${bgColor}; border: 1px solid gray; width: ${COLOR_BLOCK_SIZE}; aspect-ratio: 1 / 1; display: grid; place-items: center; overflow: hidden; ` .then(colorBlock => { container.appendChild(colorBlock); let centerDiv = styleTags.div` display: flex; flex-direction: column; overflow: hidden; max-width: 90%; `; colorBlock.appendChild(centerDiv); let colorNameDiv = styleTags.div` flex: 1; font-size: 1.2em; min-height: 2em; overflow: scroll; text-align: center; color: ${candidate.hex} `; colorNameDiv.textContent = candidate.name.toLowerCase(); centerDiv.appendChild(colorNameDiv); let p = styleTags.p` margin: 0; color: gray; text-align: center; `; p.textContent = candidate.hex; centerDiv.appendChild(p); }); }); });

修改十六进制控件中的颜色值,筛选结果会随之而改变。

这意味着,对于我们所指定的背景颜色,在AAA级标准下,我们可以安全地选用上述颜色作为前景颜色,在视觉上没有任何问题。