CMYJ
CMYJ

Reputation: 172

Convert a number between 1 and 16777215 to a colour value

I'm trying to convert a number between 1 and 16,777,215 to any colour format (RGB/HSL/HEX) that increments through the colour spectrum using Javascript/jQuery.

The number 16,777,215 is the total possible combinations of RGB(255,255,255) which is 32 bit colour.

I initially thought converting the value to a hex value using toString(16) would increment through the spectrum, however as the number increases it seems to work through different lightness values instead and flashes. An example of this unwanted behaviour is here http://jsfiddle.net/2z82auka/

var colour = 16777215;
window.setInterval(function(){
    colour -= 1000;
    $('body').css({background:'#' + colour.toString(16)});
}, 50);

How can I convert a value between 1 and 16777215 to a colour on the colour spectrum shown below?

Wanted outcome

Upvotes: 5

Views: 16532

Answers (10)

DeNitE Appz
DeNitE Appz

Reputation: 1011

This works for me...

export function intToHex(colorNumber)
{
    function toHex(n) {
      n = n.toString(16) + '';
      return n.length >= 2 ? n : new Array(2 - n.length + 1).join('0') + n;
    }

    var r = toHex(Math.floor( Math.floor(colorNumber / 256) / 256 ) % 256),
        g = toHex(Math.floor( colorNumber / 256 ) % 256),
        b = toHex(colorNumber % 256);
    return '#' + r + g + b; 
}

Upvotes: 0

Drakes
Drakes

Reputation: 23660

The code below will do exactly what you want - it'll give you vibrant colors of the color spectrum exactly as the image below, and to prove it, the demo will print out the integer values beside the color. The result will look like this. Please use the rainbow function in your setInterval code.

spectrum

var colours = 16777215;

function rainbow(numOfSteps, step) {
	var r, g, b;
	var h = 1 - (step / numOfSteps);
	var i = ~~(h * 6);
	var f = h * 6 - i;
	var q = 1 - f;
	switch(i % 6){
		case 0: r = 1, g = f, b = 0; break;
		case 1: r = q, g = 1, b = 0; break;
		case 2: r = 0, g = 1, b = f; break;
		case 3: r = 0, g = q, b = 1; break;
		case 4: r = f, g = 0, b = 1; break;
		case 5: r = 1, g = 0, b = q; break;
	}
	var c = "#" + ("00" + (~ ~(r * 235)).toString(16)).slice(-2) + ("00" + (~ ~(g * 235)).toString(16)).slice(-2) + ("00" + (~ ~(b * 235)).toString(16)).slice(-2);
	return (c);
}

function render(i) {
	var item = "<li style='background-color:" + rainbow(colours, i) + "'>" + i + "</li>";
	$("ul").append(item);
}

function repeat(fn, times) {
	for (var i = 0; i < times; i+=10000) fn(i);
}

repeat(render, colours);
li {
  font-size:8px;
  height:10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<ul></ul>

(I can't take credit for this code, but I can take credit for not giving up and settling for jerky color changes. Ref: https://gist.github.com/ruiwen/6163115)

Upvotes: 7

Populus
Populus

Reputation: 7680

My implementation....

var r = 255;
var g = 0;
var b = 0;
var stage = 1;
var step = 5;

var int = setInterval(function () {
    if (stage == 1) {
        g += step;
        if (g >= 255) {
            g = 255;
            stage = 2;
        }
    } else if (stage == 2) {
        r -= step;
        if (r <= 0) {
            r = 0;
            stage = 3;
        }
    } else if (stage == 3) {
        b += step;
        if (b >= 255) {
            b = 255;
            stage = 4;
        }
    } else if (stage == 4) {
        g -= step;
        if (g <= 0) {
            g = 0
            stage = 5;
        }
    } else if (stage == 5) {
        r += step;
        if (r >= 255) {
            r = 255;
            stage = 6;
        }
    } else if (stage == 6) {
        b -= step;
        if (b <= 0) {
            b = 0;
            clearInterval(int);
        }
    }

    //console.log(r,g,b);
    $('body').css('background-color', 'RGB('+r+','+g+','+b+')');
}, 10);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Upvotes: 0

Becuzz
Becuzz

Reputation: 6866

The picture you've shown suggests you really just want to rotate through a set of continuous colors, not every possible rgb color (since many of them essentially look white or black). I would suggest using HSV as a base instead of RGB. Trying to increment a number that represents an RGB value will lead to the stuttering you see (like @Trott pointed out, going from 0000ff to 000100 jumps from a blue to a black).

Try something like this (Fiddle):

$(document).ready(function(){

    var h = 0;
    window.setInterval(function(){
        h += .01;
        if (h >= 1) h-=1;

        var rgbColor = HSVtoRGB(h, 1, 1);
        var colorString = '#' + convertComponentToHex(rgbColor.r)
                                + convertComponentToHex(rgbColor.g) 
                                + convertComponentToHex(rgbColor.b);
        $('body').css({background:colorString});
    }, 50);
});
function convertComponentToHex(v) {
    return ("00" + v.toString(16)).substr(-2);
}
function HSVtoRGB(h, s, v) {
    var r, g, b, i, f, p, q, t;
    if (h && s === undefined && v === undefined) {
        s = h.s, v = h.v, h = h.h;
    }
    i = Math.floor(h * 6);
    f = h * 6 - i;
    p = v * (1 - s);
    q = v * (1 - f * s);
    t = v * (1 - (1 - f) * s);
    switch (i % 6) {
        case 0: r = v, g = t, b = p; break;
        case 1: r = q, g = v, b = p; break;
        case 2: r = p, g = v, b = t; break;
        case 3: r = p, g = q, b = v; break;
        case 4: r = t, g = p, b = v; break;
        case 5: r = v, g = p, b = q; break;
    }
    return {
        r: Math.floor(r * 255),
        g: Math.floor(g * 255),
        b: Math.floor(b * 255)
    };
}

(Thanks to this SO answer for the conversion code. I was too lazy to go figure it out for myself.)

Upvotes: 0

ssube
ssube

Reputation: 48267

You have the hue value, so you need to turn that into the various color formats using fixed brightness and saturation.

To properly scale the hue from [1, 16777215] to a [0, 1] scale, you'll need to do (x - 1) / 16777215. Take this number and feed it into hsl2rgb (here's a JS implementation) with a high lum and relatively high sat.

Something like so:

// From this answer: https://stackoverflow.com/a/9493060/129032
function hslToRgb(h, s, l) {
  var r, g, b;

  if (s == 0) {
    r = g = b = l; // achromatic
  } else {
    var hue2rgb = function hue2rgb(p, q, t) {
      if (t < 0) t += 1;
      if (t > 1) t -= 1;
      if (t < 1 / 6) return p + (q - p) * 6 * t;
      if (t < 1 / 2) return q;
      if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
      return p;
    }

    var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
    var p = 2 * l - q;
    r = hue2rgb(p, q, h + 1 / 3);
    g = hue2rgb(p, q, h);
    b = hue2rgb(p, q, h - 1 / 3);
  }

  return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}

function scaleHue(hue) {
  return ((hue - 1) / 16777215);
}

var colour = 0;
window.setInterval(function() {
  colour = (colour + 100000) % 16777215;
  var hue = scaleHue(colour);
  var current = hslToRgb(hue, 0.8, 0.8);
  $('body').css({
    background: '#' + current[0].toString(16) + current[1].toString(16) + current[2].toString(16)
  });
}, 50);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

I increased the step from 1000 to 100000 to make the demo more obvious.

Upvotes: 2

mostlydev
mostlydev

Reputation: 723

Marrying this answer with Drake's:

function colorNumberToHex(colorNumber) {
    function toHex(n) {
      n = n.toString(16) + '';
      return n.length >= 2 ? n : new Array(2 - n.length + 1).join('0') + n;
    }

    var r = toHex(colorNumber % 256),
        g = toHex(Math.floor( colorNumber / 256 ) % 256),
        b = toHex(Math.floor( Math.floor(colorNumber / 256) / 256 ) % 256);
    return '#' + r + g + b; 
}

Upvotes: 0

legui
legui

Reputation: 192

Something like that ?

<script>
function intToHex(colorNumber) {
    var R = (colorNumber - (colorNumber%65536)) / 65536;
    var G = ((colorNumber - R*65536) - ((colorNumber - R*65536)%256)) / 256;
    var B = colorNumber - R*65536 - G*256;
    var RGB = R.toString(16) + G.toString(16) + B.toString(16);
    return RGB;
}
</script>

Upvotes: -1

Trott
Trott

Reputation: 70075

Sticking to RGB: Always incrementing by one will not result in a steady grade through the spectrum. For example, when you go from #0000ff (which is blue) to that +1, you end up at #000100, which is essentially black.

Instead, you will probably want to do something more like incrementing each of the three values (the R value, the G value, and the B value) by one. However, that will omit many, many colors. But if smoothness is what you value over comprehensiveness, that's a simple way to get there.

@nada points out that this will give you an awful lot of grey. If you want to avoid that, you can try variations like: increment R until it can't be incremented anymore. Leave it at max value while you increment G until it hits max, then increment B to max. Now reverse it: Decrement R to minimum, then G, then B. This will still miss a ton of colors (in fact, it will miss most colors), but it should be smooth and it should avoid being nothing but grey.

Although this will work (if you don't mind missing most colors), I'm sure there is a better solution. I hope someone weighs in with it. I'm very curious.

Upvotes: 2

Jack Wild
Jack Wild

Reputation: 2122

Convert to range the initial value from 1 > 16777216 from 0 > 360

Technique here: Convert a number range to another range, maintaining ratio

Then use the HSL colour model, and increment from H0 S100 L100 > H360 S100 L100

Upvotes: 2

Jamiec
Jamiec

Reputation: 136104

Generally, this is the formula for converting an integer to rgba

r = (val)&0xFF;
g = (val>>8)&0xFF;
b = (val>>16)&0xFF;
a = (val>>24)&0xFF;

Expressed as javascript

function ToRGBA(val){
   var r = (val)&0xFF;
   var g = (val>>8)&0xFF;
   var b = (val>>16)&0xFF;
   var a = (val>>24)&0xFF;   
   return "rgb(" + r + "," + g + "," + b + ")";
}

Updated fiddle: http://jsfiddle.net/2z82auka/2/

Upvotes: -1

Related Questions