算法:如何使用RGB值从红色渐变到绿色通过黄色?

73
我想根据0到100的值显示颜色。在一端(100),它是纯红色,在另一端(0),是纯绿色。在中间(50),我想让它变成黄色。
并且我希望颜色逐渐从一个渐变到另一个,如在75处,颜色为一半红色和一半黄色等等。
如何编写RGB值以反映这种渐变?谢谢。

3
WPF?WinForms?你要将渐变应用在哪里? - Alex Aza
18个回答

101

我有同样的需求,我刚刚用这个方法解决了:

myColor = new Color(2.0f * x, 2.0f * (1 - x), 0);
解释: 与其关注[0-255]范围,让我们关注颜色分量的[0.0-1.0]范围:
绿色=0.0,1.0,0.0 黄色=1.0,1.0,0.0 红色=1.0,0.0,0.0
如果只将绿色分量从一端缩放到另一端,然后用相同的方法处理红色分量(但向后移动),则会得到丑陋和不均匀的颜色分布。
为了使它看起来漂亮,我们可以编写很多代码,或者我们可以更聪明一些。
仔细观察单个组件,您会发现我们可以将范围分成两个相等的部分:在第一个部分中,我们将红色分量从0.0增加到1.0,将绿色保持为1.0,蓝色保持为0.0;在第二个部分中,我们降低绿色分量,将其他两个组件保持不变。我们可以利用任何高于1.0的值都将被视为1.0的事实来简化代码,通过将我们的值最大化来利用这一点。假设您的x值的范围从0.00(0%)到1.00(100%),则可以将其乘以2以让其超过颜色组件的1.0限制。现在,您的组件从0.0到2.0(红色)以及从2.0到0.0(绿色)进行设置。让它们被剪裁到[0.0-1.0]范围内,然后就可以了。
如果x移动到另一个范围(如[0-100]),则需要选择适当的因子,而不是2。

非常感谢。当我发现自己处于这种情况时,我想到了这种方法,因为其他解决方案对于这样的任务来说似乎太复杂了。 - Giorgio Aresu
2
非常适合我们的需求。我们使用JavaScript进行计算,从红色到白色再到绿色。这增加了一个有趣的变化,因为黄色组件通过比例从0.0到1.0再回到0.0。这可以通过以下公式计算:yellowValue = 1 - Math.abs(value - 0.5) * 2; - LarzStarz
4
@GiorgioAresu,我采用了你的原则,并将其应用于一个在codepen上找到的实用程序:http://codepen.io/ahopkins/full/beNWWp/ - Adam Hopkins
5
完美运行!我使用Python(PyQt5),稍有修改: QColor(min(255,2*255*(temp)),min(255,2*255*(1-temp)),0) - laserpython
在JS中,我将其改为: `` `const red = Math.min(2-(2 *分数),1)* 255; const green = Math.min(2 * Fraction,1)* 255;``` - Ansjovis86
显示剩余8条评论

50

颜色的RGB值为:

  • 红色 255, 0, 0
  • 黄色 255, 255, 0
  • 绿色 0, 255, 0

在红色和黄色之间,等距离地将绿色通道增加,直到达到255。在黄色和绿色之间,等距离地从红色通道减去。


颜色不是线性的,从0到64的变化比从191到255的变化更大,这在LED显示器上尤为明显,因为它们的黑色更黑。 - Ferrybig
2
@Ferrybig,考虑到您的评论,什么是适当的插值公式? - jippyjoe4

21

这里提供了一种非常简单的颜色分量线性插值方法,可能可以满足您的需求。

public Color GetBlendedColor(int percentage)
{
    if (percentage < 50)
        return Interpolate(Color.Red, Color.Yellow, percentage / 50.0);
    return Interpolate(Color.Yellow, Color.Lime, (percentage - 50) / 50.0);
}

private Color Interpolate(Color color1, Color color2, double fraction)
{
    double r = Interpolate(color1.R, color2.R, fraction);
    double g = Interpolate(color1.G, color2.G, fraction);
    double b = Interpolate(color1.B, color2.B, fraction);
    return Color.FromArgb((int)Math.Round(r), (int)Math.Round(g), (int)Math.Round(b));
}

private double Interpolate(double d1, double d2, double fraction)
{
    return d1 + (d2 - d1) * fraction;
}

3
在 Interpolate 函数中,假设 d2 是您的目标颜色,d1 是您的起始颜色,应该是 (d2 - d1) 而不是 (d1 - d2)。 - Tincan
这对我在自定义颜色和修复d1d2方面帮助很大。 - Christian Ivicevic

18

我不懂C#,所以这个答案只是一个建议性的方法。让x表示从0100的整数。类似这样的代码应该可以工作:

red   = (x > 50 ? 1-2*(x-50)/100.0 : 1.0);
green = (x > 50 ? 1.0 : 2*x/100.0);
blue  = 0.0

这个想法是从红色(1.0,0.0,0.0)开始。然后增加绿色变成黄色:(1.0,1.0,0.0)。接着减少红色变成绿色:(0.0,1.0,0.0)

编辑:下面是C#代码:

static Color GetColorFromRedYellowGreenGradient(double percentage)
{
    var red = (percentage > 50 ? 1 - 2 * (percentage - 50) / 100.0 : 1.0) * 255;
    var green = (percentage > 50 ? 1.0 : 2 * percentage / 100.0) * 255;
    var blue = 0.0;
    Color result = Color.FromArgb((int)red, (int)green, (int)blue);
    return result;
}

这更加直接明了。 - Siwei

9

简化的扩展方法;

public static Color Interpolate(this Color source, Color target, double percent)
{
    var r = (byte)(source.R + (target.R - source.R) * percent);
    var g = (byte)(source.G + (target.G - source.G) * percent);
    var b = (byte)(source.B + (target.B - source.B) * percent);

    return Color.FromArgb(255, r, g, b);
}

使用方法:

var low = 33.0;
var high = 100.0;
var color = Color.Red.Interpolate(Color.Green, low / high);

7

在我尝试了一段时间更真实的颜色后,这是我的公式:

public Color GetColorOf(double value, double minValue, double maxValue)
{
    if (value == 0 || minValue == maxValue) return Color.White;

    var g = (int)(240 * value / maxValue);
    var r = (int)(240 * value / minValue);

    return (value > 0
        ? Color.FromArgb(240 - g, 255 - (int)(g * ((255 - 155) / 240.0)), 240 - g)
        : Color.FromArgb(255 - (int)(r * ((255 - 230) / 240.0)), 240 - r, 240 - r));
}
  • 当数值为0(或NULL)或最大值等于最小值时,背景色为无颜色(即Color.White)。
  • 对于所有正值,您将获得在RGB(240, 255, 240)和RGB(0, 155, 0)之间均匀分布的绿色。
  • 对于所有负值,您将获得在RGB(255, 240, 240)和RGB(230, 0, 0)之间均匀分布的红色。

heat map


1
这并没有回答原始问题。它不仅使用了白色而非黄色,而且操作的数字范围也与要求的不同。 - Setsu
1
+1 是因为有一个适合我的绿白红算法,但实际上并没有回答 OP 的问题。 - Benj
1
不管这回答了什么问题...这真是天才。 - Hambone

5

1
我为UIColor编写了一个Swift 4扩展,可以将百分比(0-100)转换为从红色到绿色的UIColor。我目前在分析应用程序中将其用于进度条。以下是一个示例:
let myNewColor = UIColor().greenRedProgress(percent: Int))

这是浏览器扩展。
extension UIColor{

func greenRedProgress(percent: Int) -> UIColor{
    let modVal = CGFloat((Double(percent).truncatingRemainder(dividingBy: 50) / 50) * 255)
    if percent <= 0{
        return UIColor(red: 1.0, green: 0, blue: 0, alpha: 1)
    }else if percent >= 100{
        return UIColor(red: 0, green: 1.0, blue: 0, alpha: 1)
    }else{
        switch percent{
            case 1..<50: return UIColor(red: 1.0, green: (modVal/255), blue: 0, alpha: 1)
            case 51..<100: return UIColor(red: (255 - modVal)/255, green: 1.0, blue: 0, alpha: 1)
            case 50: return UIColor(red: 1.0, green: 1.0, blue: 0, alpha: 1)
            default: return UIColor(red: 0, green: 1.0, blue: 0, alpha: 1)
        }
    }
}}

1

1
这个方法(使用c#编写,但可以轻松地翻译成其他语言)将接受一个百分比和颜色列表,并根据您的百分比返回渐变中的颜色。当您传入颜色时,它们需要按从0到100的值的顺序排列(因此您希望按照绿色、黄色、红色的顺序传递)。如果您发现想要在中间使用不同或更多的颜色,只需将它们添加到颜色列表中,按您希望它们出现的顺序传递即可。
public Color ColorBasedOnPercent(decimal percent, params Color[] colors)
    {
        if (colors.Length == 0)
        {
            //I am using Transparent as my default color if nothing was passed
            return Color.Transparent;
        }
        if (percent > 1)
        {
            percent = percent / 100;
        }

        //find the two colors within your list of colors that the percent should fall between
        var colorRangeIndex = (colors.Length - 1) * percent;
        var minColorIndex = (int)Math.Truncate(colorRangeIndex);
        var maxColorIndex = minColorIndex + 1;
        var minColor = colors[minColorIndex];

        if (maxColorIndex < colors.Length)
        {
            var maxColor = colors[maxColorIndex];

            //get the differences between all the color values for the two colors you are fading between
            var aScale = maxColor.A - minColor.A;
            var redScale = maxColor.R - minColor.R;
            var greenScale = maxColor.G - minColor.G;
            var blueScale = maxColor.B - minColor.B;

            //the decimal distance of how "far" this color should be from the minColor in the range
            var gradientPct = colorRangeIndex - minColorIndex;

            //for each piece of the color (ARGB), add a percentage(gradientPct) of the distance between the two colors
            int getRGB(int originalRGB, int scale) => (int)Math.Round(originalRGB + (scale * gradientPct));

            return Color.FromArgb(getRGB(minColor.A, aScale), getRGB(minColor.R, redScale), getRGB(minColor.G, greenScale), getRGB(minColor.B, blueScale));
        }
        return minColor;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接