根据对比度调整颜色
Adjust color based on contrast
对于我正在开发的 mod,我想合并播放器的主题颜色并使用它们生成 UI 元素。但是,我 运行 遇到了一个问题,即并非所有颜色主题都具有提供 good contrast ratio as outlined in 1.4.3 Contrast (Minimum) of Web Content Accessibility Guidelines (WCAG) 2.1.
的颜色
我目前可以使用以下方法检查对比度:
float RelativeLuminance(Color color)
{
float ColorPartValue(float part)
{
return part <= 0.03928f ? part / 12.92f : Mathf.Pow((part + 0.055f) / 1.055f, 2.4f);
}
var r = ColorPartValue(color.r);
var g = ColorPartValue(color.g);
var b = ColorPartValue(color.b);
var l = 0.2126f * r + 0.7152f * g + 0.0722f * b;
return l;
}
private float ColorContrast(Color a, Color b)
{
float result = 0f;
var La = RelativeLuminance(a) + 0.05f;
var Lb = RelativeLuminance(b) + 0.05f;
result = Mathf.Max(La, Lb) / Mathf.Min(La, Lb);
return result;
}
我使用找到的颜色对比度来确定初始文本颜色是否足够好。
public Color GetContrastingColors(Color backgroundColor, Color textColor)
{
Color contrastColor;
// See if we have good enough contrast already
if (!(ColorContrast(backgroundColor, textColor) < 4.5f))
{
return textColor;
}
Color.RGBToHSV(textColor, out var textH, out var textS, out var textV);
Color.RGBToHSV(backgroundColor, out var bgH, out var bgS, out var bgV);
// Modify textV by some value to provide enough contrast.
contrastColor = Color.HSVToRGB(textH, textS, textV);
return contrastColor;
}
但是,我不确定如何调整颜色以使文本颜色变亮(或变暗)到足以达到 4.5:1 的对比度。最初,我正在考虑将亮度和对比度方程的代数计算到 sRGB
值乘以某个值 X 的点。不过我记得 HSV,调整颜色的亮度似乎更简单我。问题是,我不确定如何比较 2 种 HSV 颜色的对比度,更不用说使用它们的值来操纵颜色的亮度以达到所需的对比度。
我目前的想法是做这样的蠢事:
float targetL;
bool brighter = false;
var backL = RelativeLuminance(backgroundColor);
var textL = RelativeLuminance(textColor);
var ratio = 4.5f;
// Try to go in the direction of brightness originally.
if (textL > backL)
{
targetL = ((backL + 0.05f) * ratio) - 0.05f;
brighter = true;
if (targetL > 1f)
{
targetL = ((backL + 0.05f) / ratio) - 0.05f;
brighter = false;
}
}
else
{
targetL = ((backL + 0.05f) / ratio) - 0.05f;
if (targetL > 0f)
{
targetL = ((backL + 0.05f) * ratio) - 0.05f;
brighter = true;
}
}
Color adjustedColor = textColor;
while ((!brighter && textL > targetL) || (brighter && textL < targetL))
{
Color.RGBToHSV(adjustedColor, out var textH, out var textS, out var textV);
textV += brighter ? 0.01f : -0.01f;
adjustedColor = Color.HSVToRGB(textH, textS, textV);
textL = RelativeLuminance(adjustedColor);
}
contrastColor = adjustedColor;
但这并不是很有效,那么我怎样才能操纵文本颜色以使其“保持相同”但提供足够的对比度?
编辑:
为了让我尝试做的事情有更多背景,假设我有以下 4 种颜色作为玩家的主题。
根据 HTML 代码,即:
#32263d
#3d1c70
#7347b6
#320d68
我想在为他们创建 UI 时从他们的主题中加入其中 2 种颜色。然而,并不是所有的都容易区分,你可以在这里看到这种情况下的各种对比:
现在每个主题都包含较深和较浅的颜色,就像本例中的中间 2 行一样,但也像本例一样,最终用户可能无法始终阅读它们的对比度。继续这个例子,在这种情况下,我们将使用 #32263d
和 #7347b6
来构建我们的 UI.
虽然我可以尝试随机创建一个相似的紫色阴影,但我想让它尽可能接近原始颜色并使其变亮。我们可以在这里看到它在各种光线下的样子:
如果我们将 #7347b6
设置为 #a163ff
处的最大亮度,我们现在得到以下对:
虽然比以前好,但这仍然只是 3.88 : 1 的对比度。所以现在我想按比例缩小 #32263d
的亮度。如果我们将它减少到 #251B2D
,那么我们最终会得到:
这两种新颜色的颜色对比度为 4.51 : 1。
现在,我可以手动浏览每个主题,但考虑到它们的数量,我更愿意编写一个算法来动态生成更新的颜色。
查看我对
的回答
你可以跳过我谈论对比度公式的部分,因为你已经知道了,但我谈论的是如何调整颜色以获得更好的对比度。
如果我真的根据之前的答案编写我的建议,我会更有效率,而不是从每个 RGB 分量中加或减 1 并重新计算亮度,我可能会 add/subtract 10 并重新计算.如果反差不够,再做10次。一旦我获得足够的对比度,我就可以重新调整值,也许每次调整 2,在相反的方向,直到我接近 4.5 而不会下降。
毕竟我的代码最终使用了一个循环。虽然 的答案接近我想要的,但我发现将所有 RGB 分量调整相同的量并不是我想要的,因为它实际上影响了色调,所以我最终改用了 HSV。
public Color[] GetContrastingColors(Color backgroundColor, Color textColor, float ratio)
{
Color[] colors = new Color[2];
var backL = RelativeLuminance(backgroundColor);
var textL = RelativeLuminance(textColor);
if (textL > backL)
{
colors[0] = textColor;
colors[1] = backgroundColor;
}
else
{
colors[1] = textColor;
colors[0] = backgroundColor;
}
// See if we have good enough contrast already
if (!(ColorContrast(backgroundColor, textColor) < ratio))
{
return colors;
}
Color.RGBToHSV(colors[0], out var lightH, out var lightS, out var lightV);
Color.RGBToHSV(colors[1], out var darkH, out var darkS, out var darkV);
// If the darkest color can be darkened enough to have enough contrast after brightening the color.
if (ColorContrast(Color.HSVToRGB(darkH, darkS, 0f), Color.HSVToRGB(lightH, lightS, 1f)) >= ratio)
{
var lightDiff = 1f - lightV;
var darkDiff = darkV;
var steps = new float[] { 0.12f, 0.1f, 0.08f, 0.05f, 0.04f, 0.03f, 0.02f, 0.01f, 0.005f };
var step = 0;
var lightRatio = (lightDiff / (lightDiff + darkDiff));
var darkRatio = (darkDiff / (lightDiff + darkDiff));
while (ColorContrast(Color.HSVToRGB(lightH, lightS, lightV), Color.HSVToRGB(darkH, darkS, darkV)) < ratio)
{
while (ColorContrast(Color.HSVToRGB(lightH, lightS, lightV + lightRatio * steps[step]), Color.HSVToRGB(darkH, darkS, darkV - darkRatio * steps[step])) > ratio && step < steps.Length - 1)
{
step++;
}
lightV += lightRatio * steps[step];
darkV -= darkRatio * steps[step];
}
colors[0] = Color.HSVToRGB(lightH, lightS, lightV);
colors[1] = Color.HSVToRGB(darkH, darkS, darkV);
}
// Fall back to using white.
else
{
colors[0] = Color.white;
while (ColorContrast(Color.white, Color.HSVToRGB(darkH, darkS, darkV)) < ratio)
{
darkV -= 0.01f;
}
colors[1] = Color.HSVToRGB(darkH, darkS, darkV);
}
return colors;
}
对于我正在开发的 mod,我想合并播放器的主题颜色并使用它们生成 UI 元素。但是,我 运行 遇到了一个问题,即并非所有颜色主题都具有提供 good contrast ratio as outlined in 1.4.3 Contrast (Minimum) of Web Content Accessibility Guidelines (WCAG) 2.1.
的颜色我目前可以使用以下方法检查对比度:
float RelativeLuminance(Color color)
{
float ColorPartValue(float part)
{
return part <= 0.03928f ? part / 12.92f : Mathf.Pow((part + 0.055f) / 1.055f, 2.4f);
}
var r = ColorPartValue(color.r);
var g = ColorPartValue(color.g);
var b = ColorPartValue(color.b);
var l = 0.2126f * r + 0.7152f * g + 0.0722f * b;
return l;
}
private float ColorContrast(Color a, Color b)
{
float result = 0f;
var La = RelativeLuminance(a) + 0.05f;
var Lb = RelativeLuminance(b) + 0.05f;
result = Mathf.Max(La, Lb) / Mathf.Min(La, Lb);
return result;
}
我使用找到的颜色对比度来确定初始文本颜色是否足够好。
public Color GetContrastingColors(Color backgroundColor, Color textColor)
{
Color contrastColor;
// See if we have good enough contrast already
if (!(ColorContrast(backgroundColor, textColor) < 4.5f))
{
return textColor;
}
Color.RGBToHSV(textColor, out var textH, out var textS, out var textV);
Color.RGBToHSV(backgroundColor, out var bgH, out var bgS, out var bgV);
// Modify textV by some value to provide enough contrast.
contrastColor = Color.HSVToRGB(textH, textS, textV);
return contrastColor;
}
但是,我不确定如何调整颜色以使文本颜色变亮(或变暗)到足以达到 4.5:1 的对比度。最初,我正在考虑将亮度和对比度方程的代数计算到 sRGB
值乘以某个值 X 的点。不过我记得 HSV,调整颜色的亮度似乎更简单我。问题是,我不确定如何比较 2 种 HSV 颜色的对比度,更不用说使用它们的值来操纵颜色的亮度以达到所需的对比度。
我目前的想法是做这样的蠢事:
float targetL;
bool brighter = false;
var backL = RelativeLuminance(backgroundColor);
var textL = RelativeLuminance(textColor);
var ratio = 4.5f;
// Try to go in the direction of brightness originally.
if (textL > backL)
{
targetL = ((backL + 0.05f) * ratio) - 0.05f;
brighter = true;
if (targetL > 1f)
{
targetL = ((backL + 0.05f) / ratio) - 0.05f;
brighter = false;
}
}
else
{
targetL = ((backL + 0.05f) / ratio) - 0.05f;
if (targetL > 0f)
{
targetL = ((backL + 0.05f) * ratio) - 0.05f;
brighter = true;
}
}
Color adjustedColor = textColor;
while ((!brighter && textL > targetL) || (brighter && textL < targetL))
{
Color.RGBToHSV(adjustedColor, out var textH, out var textS, out var textV);
textV += brighter ? 0.01f : -0.01f;
adjustedColor = Color.HSVToRGB(textH, textS, textV);
textL = RelativeLuminance(adjustedColor);
}
contrastColor = adjustedColor;
但这并不是很有效,那么我怎样才能操纵文本颜色以使其“保持相同”但提供足够的对比度?
编辑:
为了让我尝试做的事情有更多背景,假设我有以下 4 种颜色作为玩家的主题。
根据 HTML 代码,即:
#32263d | #3d1c70 |
#7347b6 | #320d68 |
我想在为他们创建 UI 时从他们的主题中加入其中 2 种颜色。然而,并不是所有的都容易区分,你可以在这里看到这种情况下的各种对比:
现在每个主题都包含较深和较浅的颜色,就像本例中的中间 2 行一样,但也像本例一样,最终用户可能无法始终阅读它们的对比度。继续这个例子,在这种情况下,我们将使用 #32263d
和 #7347b6
来构建我们的 UI.
虽然我可以尝试随机创建一个相似的紫色阴影,但我想让它尽可能接近原始颜色并使其变亮。我们可以在这里看到它在各种光线下的样子:
如果我们将 #7347b6
设置为 #a163ff
处的最大亮度,我们现在得到以下对:
虽然比以前好,但这仍然只是 3.88 : 1 的对比度。所以现在我想按比例缩小 #32263d
的亮度。如果我们将它减少到 #251B2D
,那么我们最终会得到:
这两种新颜色的颜色对比度为 4.51 : 1。
现在,我可以手动浏览每个主题,但考虑到它们的数量,我更愿意编写一个算法来动态生成更新的颜色。
查看我对
你可以跳过我谈论对比度公式的部分,因为你已经知道了,但我谈论的是如何调整颜色以获得更好的对比度。
如果我真的根据之前的答案编写我的建议,我会更有效率,而不是从每个 RGB 分量中加或减 1 并重新计算亮度,我可能会 add/subtract 10 并重新计算.如果反差不够,再做10次。一旦我获得足够的对比度,我就可以重新调整值,也许每次调整 2,在相反的方向,直到我接近 4.5 而不会下降。
毕竟我的代码最终使用了一个循环。虽然
public Color[] GetContrastingColors(Color backgroundColor, Color textColor, float ratio)
{
Color[] colors = new Color[2];
var backL = RelativeLuminance(backgroundColor);
var textL = RelativeLuminance(textColor);
if (textL > backL)
{
colors[0] = textColor;
colors[1] = backgroundColor;
}
else
{
colors[1] = textColor;
colors[0] = backgroundColor;
}
// See if we have good enough contrast already
if (!(ColorContrast(backgroundColor, textColor) < ratio))
{
return colors;
}
Color.RGBToHSV(colors[0], out var lightH, out var lightS, out var lightV);
Color.RGBToHSV(colors[1], out var darkH, out var darkS, out var darkV);
// If the darkest color can be darkened enough to have enough contrast after brightening the color.
if (ColorContrast(Color.HSVToRGB(darkH, darkS, 0f), Color.HSVToRGB(lightH, lightS, 1f)) >= ratio)
{
var lightDiff = 1f - lightV;
var darkDiff = darkV;
var steps = new float[] { 0.12f, 0.1f, 0.08f, 0.05f, 0.04f, 0.03f, 0.02f, 0.01f, 0.005f };
var step = 0;
var lightRatio = (lightDiff / (lightDiff + darkDiff));
var darkRatio = (darkDiff / (lightDiff + darkDiff));
while (ColorContrast(Color.HSVToRGB(lightH, lightS, lightV), Color.HSVToRGB(darkH, darkS, darkV)) < ratio)
{
while (ColorContrast(Color.HSVToRGB(lightH, lightS, lightV + lightRatio * steps[step]), Color.HSVToRGB(darkH, darkS, darkV - darkRatio * steps[step])) > ratio && step < steps.Length - 1)
{
step++;
}
lightV += lightRatio * steps[step];
darkV -= darkRatio * steps[step];
}
colors[0] = Color.HSVToRGB(lightH, lightS, lightV);
colors[1] = Color.HSVToRGB(darkH, darkS, darkV);
}
// Fall back to using white.
else
{
colors[0] = Color.white;
while (ColorContrast(Color.white, Color.HSVToRGB(darkH, darkS, darkV)) < ratio)
{
darkV -= 0.01f;
}
colors[1] = Color.HSVToRGB(darkH, darkS, darkV);
}
return colors;
}