from green to red color depend on percentage
Asked Answered
S

16

122

I have a poll system and I want answers for this poll to be colored. For example: If it's 10% it would be red, if 40% it would be yellow and if 80% it would be green, so I want my javascript code to use the rgb colors to make a color according to the given percentage.

function hexFromRGB(r, g, b) {
    var hex = [
        r.toString( 16 ),
        g.toString( 16 ),
        b.toString( 16 )
    ];
    $.each( hex, function( nr, val ) {
        if ( val.length === 1 ) {
            hex[ nr ] = "0" + val;
        }
    });
    return hex.join( "" ).toUpperCase();
}  

Now I want hex from percent.

Shoshana answered 20/8, 2011 at 0:18 Comment(1)
npmjs.com/package/percent-to-hexAudet
A
123

This may be more than you need, but this lets you set up any arbitrary color map:

var percentColors = [
    { pct: 0.0, color: { r: 0xff, g: 0x00, b: 0 } },
    { pct: 0.5, color: { r: 0xff, g: 0xff, b: 0 } },
    { pct: 1.0, color: { r: 0x00, g: 0xff, b: 0 } } ];

var getColorForPercentage = function(pct) {
    for (var i = 1; i < percentColors.length - 1; i++) {
        if (pct < percentColors[i].pct) {
            break;
        }
    }
    var lower = percentColors[i - 1];
    var upper = percentColors[i];
    var range = upper.pct - lower.pct;
    var rangePct = (pct - lower.pct) / range;
    var pctLower = 1 - rangePct;
    var pctUpper = rangePct;
    var color = {
        r: Math.floor(lower.color.r * pctLower + upper.color.r * pctUpper),
        g: Math.floor(lower.color.g * pctLower + upper.color.g * pctUpper),
        b: Math.floor(lower.color.b * pctLower + upper.color.b * pctUpper)
    };
    return 'rgb(' + [color.r, color.g, color.b].join(',') + ')';
    // or output as hex if preferred
};
Alienable answered 20/8, 2011 at 0:46 Comment(3)
Wow, thanks Mister @Alienable ^-^ https://jsfiddle.net/JeancarloFontalvo/1sco9Lpe/3/Singly
Thanks so much for this solution. What need's to be done to make the green a bit darker like a dark-green? The solution above is hard to read on the website I'm working on!Shel
@Chris: you'd modify that last color { pct: 1.0, color: { r: 0x00, g: 0xff, b: 0 } } to a darker RGB color. For example, changing g to 0x7f will make it half as bright. I'd suggest playing around with a color picker to choose which endpoints you want, and this should blend smoothly among them.Alienable
M
164

A simple scheme using HSL along with fiddle:

function getColor(value){
    //value from 0 to 1
    var hue=((1-value)*120).toString(10);
    return ["hsl(",hue,",100%,50%)"].join("");
}

tweak saturation and luminosity as needed. and a fiddle.

function getColor(value) {
  //value from 0 to 1
  var hue = ((1 - value) * 120).toString(10);
  return ["hsl(", hue, ",100%,50%)"].join("");
}
var len = 20;
for (var i = 0; i <= len; i++) {
  var value = i / len;
  var d = document.createElement('div');
  d.textContent = "value=" + value;
  d.style.backgroundColor = getColor(value);
  document.body.appendChild(d);
}
Malka answered 24/6, 2013 at 5:21 Comment(10)
So, it's a black box. Any explanation?Halfway
From red to green. var hue=(value*120).toString(10);Quadripartite
How could you adapt this to include a min and max value? For instance, getColor(12,0,100) (getColor(value, min, max))?Natoshanatron
Scratch that, doesn't work 100%: #40111221Natoshanatron
@SaeedNeamati It sets the hue between 0 and 120 (representing red through green) based on the value you pass (0 through 1). It converts the hue to a string ("0" through "120"), then returns a string coding the color in hsl format (hue, saturation, lightness), with saturation set to 100% and lightness set to 50%.Perhaps
This should be the accepted answer, very clean. Thanks!Decoration
worth mentioning that the color is red > 0.5 > green, meaning if calculating progress with width (1 done, 0 nothing), you need to subtract 1 from the width value given: getColor( 1 - width ) {...} to achieve green at 100% progress and red at 0%. awesome function thank you!Myles
Thanks, or in ES6 const getColor = v => hsl(${((1 - v) * 120)},100%,50%); jsfiddle.net/pcmsdjL8Convery
I love stackoverflow!Fervid
Here is npm package based on your code: npmjs.com/package/percent-to-hexAudet
A
123

This may be more than you need, but this lets you set up any arbitrary color map:

var percentColors = [
    { pct: 0.0, color: { r: 0xff, g: 0x00, b: 0 } },
    { pct: 0.5, color: { r: 0xff, g: 0xff, b: 0 } },
    { pct: 1.0, color: { r: 0x00, g: 0xff, b: 0 } } ];

var getColorForPercentage = function(pct) {
    for (var i = 1; i < percentColors.length - 1; i++) {
        if (pct < percentColors[i].pct) {
            break;
        }
    }
    var lower = percentColors[i - 1];
    var upper = percentColors[i];
    var range = upper.pct - lower.pct;
    var rangePct = (pct - lower.pct) / range;
    var pctLower = 1 - rangePct;
    var pctUpper = rangePct;
    var color = {
        r: Math.floor(lower.color.r * pctLower + upper.color.r * pctUpper),
        g: Math.floor(lower.color.g * pctLower + upper.color.g * pctUpper),
        b: Math.floor(lower.color.b * pctLower + upper.color.b * pctUpper)
    };
    return 'rgb(' + [color.r, color.g, color.b].join(',') + ')';
    // or output as hex if preferred
};
Alienable answered 20/8, 2011 at 0:46 Comment(3)
Wow, thanks Mister @Alienable ^-^ https://jsfiddle.net/JeancarloFontalvo/1sco9Lpe/3/Singly
Thanks so much for this solution. What need's to be done to make the green a bit darker like a dark-green? The solution above is hard to read on the website I'm working on!Shel
@Chris: you'd modify that last color { pct: 1.0, color: { r: 0x00, g: 0xff, b: 0 } } to a darker RGB color. For example, changing g to 0x7f will make it half as bright. I'd suggest playing around with a color picker to choose which endpoints you want, and this should blend smoothly among them.Alienable
C
41

You can do this in a few lines of code (not including comments) without the need for any color maps.

function hsl_col_perc(percent, start, end) {
  var a = percent / 100,
      b = (end - start) * a,
      c = b + start;

  // Return a CSS HSL string
  return 'hsl('+c+', 100%, 50%)';
}
//Change the start and end values to reflect the hue map
//Refernece : http://www.ncl.ucar.edu/Applications/Images/colormap_6_3_lg.png

/*
Quick ref:
    0 – red
    60 – yellow
    120 – green
    180 – turquoise
    240 – blue
    300 – pink
    360 – red
*/      

Example: https://jsfiddle.net/x363g1yc/634/

No need for color maps (Unless it is a non-linear color change, which was not asked)

Warning: This is not compatible with IE8 or below. (Thanks Bernhard Fürst)

// Just used as a shortcut for below, completely optional
const red = 0,
  yellow = 60,
  green = 120,
  turquoise = 180,
  blue = 240,
  pink = 300;

function hsl_col_perc(percent, start, end) {
  var a = percent / 100,
    b = (end - start) * a,
    c = b + start;

  // Return a CSS HSL string
  return 'hsl(' + c + ', 100%, 50%)';
}

// Simple little animation
var percent = 0,
  progressDiv = document.getElementById('progress'),
  textDiv = document.getElementById('progress-text'),
  progressContainerDiv = document.getElementById('progress-container')

function step(timestamp) {
  percent = (percent < 100) ? percent + 0.5 : 0;

  // Play with me!
  let colour = hsl_col_perc(percent, red, green); //Red -> Green
  progressDiv.style.backgroundColor = colour;
  progressContainerDiv.style.borderColor = colour;
  progressDiv.style.width = percent + '%';
  textDiv.innerHTML = Math.floor(percent);
  window.requestAnimationFrame(step);
}

window.requestAnimationFrame(step);
#progress {
  width: 0%;
  white-space: nowrap;
  text-wrap: none;
  height: 50px;
}

#progress-container {
  border: solid 2px black;
  width: 200px;
}
<h1 id="progress-text"></h1>
<div id="progress-container">
  <div id="progress"></div>
</div>
Curitiba answered 24/6, 2013 at 3:36 Comment(7)
Using the HSL color model is cool but it does not work in Internet Explorer 8, unfortunately.Tonedeaf
This is true, I'll amend my answerCuritiba
Can you explain start and end in this?Natoshanatron
Start is the starting color, end is the ending color. So for example. hsl_col_perc(100,0,120) will be 100% green (refer to color table above). You could do hsl_col_perc(100,0,180) will be 100% turquoiseCuritiba
No, end is the difference between start and end color in the code above. hsl_col_perc(100, 60, 120) will result in turquoise instead of green. You need to change the second line to b = (end-start)*a, or just make it a one-liner since that would be easier to read.Slime
I've updated the example to make it much simpler to grasp how it might lookCuritiba
Thanks for leaving in the quick HSL color ref!Superpose
G
14

This method works well in this case (percent from 0 to 100):

function getGreenToRed(percent){
            r = percent<50 ? 255 : Math.floor(255-(percent*2-100)*255/100);
            g = percent>50 ? 255 : Math.floor((percent*2)*255/100);
            return 'rgb('+r+','+g+',0)';
        }
Greenland answered 6/8, 2013 at 16:15 Comment(0)
L
14

I know this is originally an old javascript question, but I got here searching for CSS only solution, so maybe it will help others equally: It's actually quite simple:

Use the percentage as a HSL color value! Red to Green spans H from 0 to 128.(so you can pump the percentage up by 1.2 if you want). Example:

background-color:hsl(perc,100%,50%);

Where perc is just the number, without the % sign.

Linguistics answered 9/1, 2020 at 12:8 Comment(0)
C
8

I've modified zmc's answer to give a gradient from green (0%) to red(100%).

const getGreenToRed = (percent) => {
  const r = (255 * percent) / 100;
  const g = 255 - (255 * percent) / 100;
  return 'rgb(' + r + ',' + g + ',0)';
};

Edit:

Just revisiting this years later... here is a more flexible implementation for any colors.

const interpolateBetweenColors = (
  fromColor,
  toColor,
  percent
) => {
  const delta = percent / 100;
  const r = Math.round(toColor.r + (fromColor.r - toColor.r) * delta);
  const g = Math.round(toColor.g + (fromColor.g - toColor.g) * delta);
  const b = Math.round(toColor.b + (fromColor.b - toColor.b) * delta);

  return `rgb(${r}, ${g}, ${b})`;
};

const patches = document.getElementById('patches');

for (let i = 0; i <= 100; i += 5) {
  const color = interpolateBetweenColors(
    { r: 0, g: 255, b: 0 },
    { r: 255, g: 0, b: 0 },
    i
  );
  
  const patch = document.createElement('div');
  patch.style.backgroundColor = color;
  patch.style.width = '100px';
  patch.style.height = '10px';
  patches.appendChild(patch);
}
<div id="patches"></div>
Crossley answered 10/4, 2020 at 13:31 Comment(1)
Best anwer so far. Thank you for providing an rgb alternative to using hsl colour values.Intradermal
T
7
function hexFromRGBPercent(r, g, b) {
    var hex = [
        Math.floor(r / 100 * 255).toString( 16 ),
        Math.floor(g / 100 * 255).toString( 16 ),
        Math.floor(b / 100 * 255).toString( 16 )
    ];
    $.each( hex, function( nr, val ) {
        if ( val.length === 1 ) {
            hex[ nr ] = "0" + val;
        }
    });
    return hex.join( "" ).toUpperCase();
}

Credit goes to andrew. He was faster.

Tommie answered 20/8, 2011 at 0:28 Comment(0)
I
5

Changing from red to green color using HLS. The value should be from 0 to 100, in this case simulating percentage (%).

function getColorFromRedToGreenByPercentage(value) {
    const hue = Math.round(value);
    return ["hsl(", hue, ", 50%, 50%)"].join("");
}
Incarcerate answered 8/10, 2018 at 19:30 Comment(1)
Very nice because it supports decent muted colors if you need them :)Bid
A
3

Based on Jacobs answer I made a loadbar. This one is from green to red, but you can change the colors. For those interested here's my code and the jsfiddle ( http://jsfiddle.net/rxR3x/ )

var percentColors = [
    { pct: 0, color: '#00FF00' },   { pct: 3, color: '#12FF00' },   { pct: 6, color: '#24FF00' },
    { pct: 10, color: '#47FF00' },  { pct: 13, color: '#58FF00' },  { pct: 16, color: '#6AFF00' },
    { pct: 20, color: '#7CFF00' },  { pct: 23, color: '#8DFF00' },  { pct: 26, color: '#9FFF00' },
    { pct: 30, color: '#B0FF00' },  { pct: 33, color: '#C2FF00' },  { pct: 36, color: '#D4FF00' },
    { pct: 40, color: '#E5FF00' },  { pct: 43, color: '#F7FF00' },  { pct: 46, color: '#FFF600' },
    { pct: 50, color: '#FFE400' },  { pct: 53, color: '#FFD300' },  { pct: 56, color: '#FFC100' },
    { pct: 60, color: '#FFAF00' },  { pct: 63, color: '#FF9E00' },  { pct: 66, color: '#FF8C00' },
    { pct: 70, color: '#FF7B00' },  { pct: 73, color: '#FF6900' },  { pct: 76, color: '#FF5700' },
    { pct: 80, color: '#FF4600' },  { pct: 83, color: '#FF3400' },  { pct: 86, color: '#FF2300' },
    { pct: 90, color: '#FF1100' },  { pct: 93, color: '#FF0000' },  { pct: 96, color: '#FF0000' },
    { pct: 100, color: '#FF0000' }
];
var getColorPercent = function(selector, percent, time){
    var i = 0;
    var percentInterval = setInterval(function() {
         i++;
         if(percent >= percentColors[i].pct) {
            console.log(percentColors[i].color);
            $(selector).css('background-color', percentColors[i].color);
        }
        if(percentColors[i].pct>=percent) {
            clearInterval(percentInterval);
        }
    }, time/25);
    $(selector).animate({width:(200/100)*percent}, time);
}
getColorPercent('#loadbar_storage', 100, 1500);

var percentColors = [
    { pct: 0, color: '#00FF00' },	{ pct: 3, color: '#12FF00' },	{ pct: 6, color: '#24FF00' },
    { pct: 10, color: '#47FF00' },	{ pct: 13, color: '#58FF00' },	{ pct: 16, color: '#6AFF00' },
    { pct: 20, color: '#7CFF00' },	{ pct: 23, color: '#8DFF00' },	{ pct: 26, color: '#9FFF00' },
    { pct: 30, color: '#B0FF00' },	{ pct: 33, color: '#C2FF00' },	{ pct: 36, color: '#D4FF00' },
    { pct: 40, color: '#E5FF00' },	{ pct: 43, color: '#F7FF00' },	{ pct: 46, color: '#FFF600' },
    { pct: 50, color: '#FFE400' },	{ pct: 53, color: '#FFD300' },	{ pct: 56, color: '#FFC100' },
    { pct: 60, color: '#FFAF00' },	{ pct: 63, color: '#FF9E00' },	{ pct: 66, color: '#FF8C00' },
    { pct: 70, color: '#FF7B00' },	{ pct: 73, color: '#FF6900' },	{ pct: 76, color: '#FF5700' },
    { pct: 80, color: '#FF4600' },	{ pct: 83, color: '#FF3400' },	{ pct: 86, color: '#FF2300' },
    { pct: 90, color: '#FF1100' },	{ pct: 93, color: '#FF0000' },	{ pct: 96, color: '#FF0000' },
    { pct: 100, color: '#FF0000' }
];
var getColorPercent = function(selector, percent, time) {
  var i = 0;
  var percentInterval = setInterval(function() {
    i++;
    if (percent >= percentColors[i].pct) {
      $(selector).css('background-color', percentColors[i].color);
    }
    if (percentColors[i].pct >= percent) {
      clearInterval(percentInterval);
    }
  }, time / 25);
  $(selector).animate({
    width: (200 / 100) * percent
  }, time);
}
getColorPercent('#loadbar', 100, 1500);
#loadbar_wrapper {
  width: 200px;
  height: 25px;
  border: 1px solid #CECECE;
}

#loadbar {
  width: 0px;
  height: 25px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="loadbar_wrapper">
  <div id="loadbar"></div>
</div>
Abirritant answered 13/4, 2013 at 15:58 Comment(0)
C
3

This is what I came up with:

function rgbify(maxval, minval, val, moreisgood) {
    var intnsty = (val - minval) / (maxval - minval);
    var r, g;
    if (moreisgood) {
        if (intnsty > 0.5) {
            g = 255;
            r = Math.round(2 * (1 - intnsty) * 255);
        } else {
            r = 255;
            g = Math.round(2 * intnsty * 255);
        }

    } else { //lessisgood
        if (intnsty > 0.5) {
            r = 255;
            g = Math.round(2 * (1 - intnsty) * 255);
        } else {
            g = 255;
            r = Math.round(2 * intnsty * 255);
        }
    }
    return "rgb(" + r.toString() + ", " + g.toString() + ", 0)";
}

jsfiddle

The moreisgood flag toggles if higher values should be red or green. maxval and minval are the threshold values for your range. val is the value to be converted to rgb

Cinema answered 25/8, 2014 at 19:8 Comment(0)
D
2

Modify these two lines in Jacob's code:

var lower = percentColors[i - 1];
var upper = percentColors[i];

to:

var lower = (i === 0) ?  percentColors[i] : percentColors[i - 1];
var upper = (i === 0) ? percentColors[i + 1] : percentColors[i];

if you want to make it work for the two extremes (i.e. 0.0 and 1.0).

Daguerre answered 8/11, 2012 at 14:57 Comment(0)
R
2

I know this is kind of bump to topic but I found one more way of doing it.

To do this you can also create a dynamic canvas of 100x1 dimension and apply gradient to it and then from function you just need to get pixel color of the percent location.

Here is the code : This is global:

/* dynamic canvas */

// this should be done once in a page so out of function, makes function faster
var colorBook = $('<canvas />')[0];
colorBook.width = 101;
colorBook.height = 1;
var ctx = colorBook.getContext("2d");
var grd = ctx.createLinearGradient(0, 0, 101, 0);
grd.addColorStop(0, "rgb(255,0,0)"); //red
grd.addColorStop(0.5, "rgb(255,255,0)"); //yellow
grd.addColorStop(1, "rgb(0,255,0)"); //green    
ctx.fillStyle = grd;
ctx.fillRect(0, 0, 101, 1);

Then the function:

function getColor(value) {
  return 'rgba(' + ctx.getImageData(Math.round(value), 0, 1, 1).data.join() + ')';
}

Demo : https://jsfiddle.net/asu09csj/

Romero answered 18/1, 2016 at 15:8 Comment(0)
U
1

HSL will work in IE8 using jquery-ui-1.10.4.

I modified jongo45's answer to accept lightness in the function.

function getColor(value, lightness) {
    //value from 0 to 1
    var hue = ((value) * 120).toString(10);
    return ["hsl(", hue, ",100%,", lightness, "%)"].join("");
}
Undulatory answered 24/6, 2014 at 21:11 Comment(0)
T
1

Mattisdada's code was really helpful for me while I was making a chart to display statistics of some quiz results. I modified it a bit to allow "clipping" of the percentage (not sure what the right term is) and also to work both ways along the colour wheel, e.g. both from green(120) to red(0) and vice versa.

function pickColourByScale(percent, clip, saturation, start, end)
{
    var a = (percent <= clip) ? 0 : (((percent - clip) / (100 - clip))),
        b = Math.abs(end - start) * a,
        c = (end > start) ? (start + b) : (start - b);
    return 'hsl(' + c + ','+ saturation +'%,50%)';
}

Basically, by setting a percentage value to clip the scaling at, everything below that value will be coloured as your start colour. It also recalculates the scaling according to 100% - clip.

Let's go through an example scenario where I enter the following values:

  • percent: 75
  • clip: 50
  • saturation: 100 (unimportant, I use this for highlighting a Chart.js chart)
  • start: 0 (red)
  • end: 120 (green)

    1. I check if percent is less than clip, and I return 0% if it is. Otherwise, I recalculate the percentage - 75% is halfway between 50% and 100%, so I get 50%. This gives me 0.5.
    2. I get the difference between start and end. You need to use Math.abs() in case your start hue value is more than your end hue value. Then I multiply the difference by the result obtained in step 1 to see how much I need to offset the start hue value.
    3. If the start value is more than the end value, then you need to move along the colour wheel in the opposite direction. Add to or subtract from the start value accordingly.

I end up with yellow, which is halfway between red and green. If I hadn't done the recalculation in step 1, I'd have ended up with a more greenish colour, which could be misleading.

Thibeault answered 5/12, 2014 at 6:59 Comment(0)
C
1

That's actually pretty simple with hsl

const color = `hsl(${score},100%,40%)`
Caerleon answered 9/12, 2022 at 13:4 Comment(0)
M
0

Using similar logic as Jacob's top answer,

I have created a Javascript color-scales that handles it for you. You are also able to input multiple color stops as well as set a transparency level.

A live demo is here: https://codepen.io/dalisc/pen/yLVXoeR

Link to the package is here: https://www.npmjs.com/package/color-scales

Example usage:

const ColorScale = require("color-scales");
let colorScale = new ColorScale(0, 100, ["#ffffff", "#000000"], 0.5);
let rgbaStr = colorScale.getColor(50).toRGBAString(); // returns "rgba(127,127,127, 0.5)"

The package is able to output a hex, rgb, or rgba string depending on your needs. It can also output a custom Color object ({r,g,b,a}) in case you need to cherry-pick the individual color components.

I came across a similar issue when trying to mimick a Tableau dashboard on a Javascript-based application. I realised it is a common feature of data visualization tools such as Tableau and Microsoft Excel, so I have created an npm package to handle it on Javascript application. If you would like to reduce your code, you could use the package.

Monotonous answered 18/2, 2021 at 16:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.