1

Is there a way to compute a color along a gradient?

For example, I have a label that I'm using as a sort of status bar with gradient background that has green (#6bba70) at one end and red (#a90329) at the other. What I'd like to do is to figure out what color is at a certain point along the gradient. For example, if the process is 27% complete, I'd like to get the color that is "27% along the gradient". Like this:

desired effect

I thought maybe that I could find the numerical shift and it would give me the correct color. The code I wrote (that doesn't work):

public static String getHTML(Date d, int shift) throws SQLException, ClassNotFoundException {
    String returnValue = "";

   returnValue += "        <style type=\"text/css\">\n"
            + "        \n"
            + "            .status{\n"
            + "                filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#6bba70', endColorstr='" + getColor(77) + "',GradientType=1 );\n"
            + "                width: " + 500 * .77 + "px;\n"
            + "                height: 5px;\n"
            + "                border: thin black solid;\n"
            + "            }\n"
            + "            .status2{\n"
            + "                filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#6bba70', endColorstr='" + getColor(100) + "',GradientType=1 );\n"
            + "                width: " + 500 + "px;\n"
            + "                height: 5px;\n"
            + "                border: thin black solid;\n"
            + "            }\n"
            + "            .status3{\n"
            + "                filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#6bba70', endColorstr='" + getColor(33) + "',GradientType=1 );\n"
            + "                width: " + 500 * .33 + "px;\n"
            + "                height: 5px;\n"
            + "                border: thin black solid;\n"
            + "            }\n"
            + "            .status4{\n"
            + "                filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#6bba70', endColorstr='" + getColor(69) + "',GradientType=1 );\n"
            + "                width: " + 500 * .69 + "px;\n"
            + "                height: 5px;\n"
            + "                border: thin black solid;\n"
            + "            }\n"
            + "        </style>"
            + "    <body>\n"
            + "        77%&nbsp;&nbsp;&nbsp;&nbsp;<label class=\"status\"></label><br><br>\n"
            + "        100%&nbsp;&nbsp;<label class=\"status2\"></label><br><br>\n"
            + "        33%&nbsp;&nbsp;&nbsp;&nbsp;<label class=\"status3\"></label><br><br>\n"
            + "        69%&nbsp;&nbsp;&nbsp;&nbsp;<label class=\"status4\"></label><br><br>\n"
            + "    </body>";
    return returnValue;
}

private static String getColor(double percentage) {
    String colorValue = "";
    int[] rgb1 = getRGB("a90329");
    int[] rgb2 = getRGB("6bba70");

    if (percentage < 100) {

        int range0 = (rgb1[0] > rgb2[0] ? rgb1[0] : rgb2[0]) - (rgb1[0] < rgb2[0] ? rgb1[0] : rgb2[0]);
        int range1 = (rgb1[1] > rgb2[1] ? rgb1[1] : rgb2[1]) - (rgb1[1] < rgb2[1] ? rgb1[1] : rgb2[1]);
        int range2 = (rgb1[2] > rgb2[2] ? rgb1[2] : rgb2[2]) - (rgb1[2] < rgb2[2] ? rgb1[2] : rgb2[2]);

        int r = (int) (rgb2[0] + Math.round(range0 * percentage / 100));
        int g = (int) (rgb2[1] + Math.round(range1 * percentage / 100));
        int b = (int) (rgb2[2] + Math.round(range2 * percentage / 100));


        System.out.println(Integer.toString(r) + "," + Integer.toString(g) + "," + Integer.toString(b));
        colorValue = String.format("#%02x%02x%02x", r, g, b);
    } else {
        colorValue = "#a90329";
    }

    return colorValue;
}

public static int[] getRGB(final String rgb) {
    final int[] ret = new int[3];
    for (int i = 0; i < 3; i++) {
        ret[i] = Integer.parseInt(rgb.substring(i * 2, i * 2 + 2), 16);
    }
    return ret;
}

That produced:

realizedEffect

Nowhere near what I wanted. Anybody have an idea of how I can achieve what I wanted?

Bob Stout
  • 1,237
  • 1
  • 13
  • 26
  • 1
    Doesn't answer the question, but you can just subtract and then take the absolute value instead of doing that mess with the `range` variables. – James Montagne Jun 30 '14 at 20:59
  • Also theres a class called `Color`, it has methods like `decode` which takes a hex string and converts it to a color. From there it has methods like `getRed`, `getBlue` and `getGreen`. – ug_ Jun 30 '14 at 21:27
  • a CSS compiler or a javaScript routine might b helpfull here :) http://codepen.io/gc-nomade/pen/ztDCn (scss used here) – G-Cyrillus Jun 30 '14 at 21:52
  • javascripted , including IE8 http://codepen.io/IE-Tests/pen/BIwJm any feedback is appreciate. – G-Cyrillus Jul 01 '14 at 12:40

4 Answers4

3

Since the gradient is a tween between two colors, I am assuming it works something like:

int[] start = getRGB("start color here");
int[] end = getRGB("end color here");
double i = 25; // Let i be the percent from start to end, 25 is arbitrary
int newred = (i / 100.0) * start[0] + (1.0 - i / 100.0) * end[0];
// I am sure you can figure out the other two

Sorry for the lazy response, but what I was trying to demonstrate is that getting a color in 'between' two colors is really just a percentage of the first color component plus the inverse percentage of the second component. At least that is how I have done gradients.

SyntaxTerror
  • 346
  • 1
  • 10
0

Colors are broken up (usually) into 3 values ranging from 0 to 255, each value representing an amount of red green or blue (respectively). You need to find the initial value for your green and red, and the final value of your green and red. For example sake, we will make it easy. Let's say your starting values are 100 green and 0 red. Then lets say your final values are 0 green and 100 red. All you need to do is map the range of each value from 0 to 100. I conveniently chose the numbers I did so the mapping is easy. So lets say you are at 25%, your green value would be 75 and your red value would be 25. Of course, it more than likely won't work out were their values are 0 to 100, but in any case, it is simple to map one range of numbers into another range. Check out this sweet SO question: How to scale down a range of numbers with a known min and max value

Community
  • 1
  • 1
TheBlindSpring
  • 631
  • 5
  • 22
0

By doing your (pseudo)absolute value with the range variables, you are losing the sign, which you actually don't want to do. Remove this and do simple subtraction and the code works:

int range0 = rgb1[0] - rgb2[0];
int range1 = rgb1[1] - rgb2[1];
int range2 = rgb1[2] - rgb2[2];

This is the generated result (IE only of course):

http://jsfiddle.net/Tz273/

James Montagne
  • 77,516
  • 14
  • 110
  • 130
  • That still doesn't give the appearance that I was looking for (but does make my code a lot cleaner). – Bob Stout Jun 30 '14 at 21:22
  • @BobStout I think your comment was before my edit. I believe the updated answer corrects your problem as the fiddle matches the image of your desired output (in IE9). – James Montagne Jun 30 '14 at 21:39
0

You could set width to labels through a jQuery loop and manage gradient via CSS. CSS + jQuery Demo

$("div[data-status]").each(function(index, value) {
   dataw = $(this).attr('data-status');
  $(this).children('label').css('width',dataw+'%');
});

Else, with A CSS compiler you may have some fun too: CSS + SCSS DEMO that generates 100 label from 1% to 100% width and generates associate style.

HTML is more or less like this:

<div data-status='2'>
  <label>status</label>
</div>

and CSS is

[data-status] {
  overflow:hidden;
  position:relative;
  box-sizing:border-box;
  margin:1em;
  padding-left:3.5em;
  background:linear-gradient(
    to right,transparent 3em,#6bba70 3em ,#a90329);
}
[data-status]:before {
  position:absolute;
  content:attr(data-status)'%';
  width:2.5em;
  left:0;
  top:0.2em;
  text-align:right;
}
[data-status] label {
  display:inline-block;
  color:rgba(0,0,0,0); 
  border:solid black;
  box-sizing:border-box;
  margin-left:-0.6em;
  box-shadow:1000px 0 0  1000px white;
}

And the SCSS routine to generate the rules (this might be what you mind, unless you do it on js/jQuery side ):

$n : 100 ;
@for $i from 0 through $n {
  @if $i > 0
  {
    [data-status="#{$i}"] label
    {
      width : #{$i + 0%}  ;
    }
  }
}

About IE8 compatibility ? DEMO

You need to add this in front of CSS proposed:

/* ie 8 */
div {
  filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#6bba70', endColorstr='#a90329',GradientType=1);
}
div label {
  outline:1000px solid white;
  color:transparent;
}
/* end ie 8 */

IE8 render

G-Cyrillus
  • 101,410
  • 14
  • 105
  • 129