Using javascript/jquery, I want to sort an array of rgba values to the colours of the visible spectrum. By doing this, like shades should be bunched together. Is there a plu
Disclosure: I'm the author of the library recommended below.
If you don't mind using a library, here's a much more concise version of Oriol's detailed response. It uses the sc-color library:
var sorted = colorArray.sort(function(colorA, colorB) {
return sc_color(colorA).hue() - sc_color(colorB).hue();
});
If your array of colors is like this:
var rgbArr = [c1, c2, c3, ...]
where each color ci
is an array of three numbers between 0 and 255
ci = [red, green, blue]
then, you can use this function to convert the colors to HSL
function rgbToHsl(c) {
var r = c[0]/255, g = c[1]/255, b = c[2]/255;
var max = Math.max(r, g, b), min = Math.min(r, g, b);
var h, s, l = (max + min) / 2;
if(max == min) {
h = s = 0; // achromatic
} else {
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch(max){
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return new Array(h * 360, s * 100, l * 100);
}
and sort them by hue
var sortedRgbArr = rgbArr.map(function(c, i) {
// Convert to HSL and keep track of original indices
return {color: rgbToHsl(c), index: i};
}).sort(function(c1, c2) {
// Sort by hue
return c1.color[0] - c2.color[0];
}).map(function(data) {
// Retrieve original RGB color
return rgbArr[data.index];
});
Here is a runnable example (thanks Ionică Bizău):
function display(container, arr) {
container = document.querySelector(container);
arr.forEach(function(c) {
var el = document.createElement("div");
el.style.backgroundColor = "rgb(" + c.join(", ") + ")";
container.appendChild(el);
})
}
function rgbToHsl(c) {
var r = c[0] / 255,
g = c[1] / 255,
b = c[2] / 255;
var max = Math.max(r, g, b),
min = Math.min(r, g, b);
var h, s, l = (max + min) / 2;
if (max == min) {
h = s = 0; // achromatic
} else {
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
return new Array(h * 360, s * 100, l * 100);
}
var rgbArr = [];
for (var i = 0; i < 100; ++i) {
rgbArr.push([
Math.floor(Math.random() * 256),
Math.floor(Math.random() * 256),
Math.floor(Math.random() * 256)
]);
}
display("#before", rgbArr);
var sortedRgbArr = rgbArr.map(function(c, i) {
// Convert to HSL and keep track of original indices
return {color: rgbToHsl(c), index: i};
}).sort(function(c1, c2) {
// Sort by hue
return c1.color[0] - c2.color[0];
}).map(function(data) {
// Retrieve original RGB color
return rgbArr[data.index];
});
display("#after", sortedRgbArr);
#before > div,
#after > div {
width: 1%;
height: 20px;
display: inline-block;
}
Random colors: <div id="before"></div>
Same colors, sorted by hue: <div id="after"></div>
sortedRgbArr
will contain the rgb colors of rgbArr
sorted more or less like the colors of the visible spectrum.
The problem is that the HSL spectrum looks like this:
Your spectrum is weird because it doesn't have all colors, such as pink.
I guess that's because pink doesn't exist in the nature, it's a combination of the colors of the opposite extremes of light's spectrum. But we have it in rgb, so you have to decide where do you want it.
Moreover, it seems that your spectrum goes from lower to higher wavelength, not frequency. But then your spectrum is a reverse of HSL's spectrum.
Replace c1.color[0] - c2.color[0]
with c2.color[0] - c1.color[0]
if you want it like your spectrum.