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级标准下,我们可以安全地选用上述颜色作为前景颜色,在视觉上没有任何问题。