convert Hsl to rgb and hex - javascript

I need a color converter to convert from hsl to rgb and hex value. I am going to do similar like this. I am using jquery and jquery ui range slider for this. Here is my code:
$(function() {
$( "#hsl_hue_range" ).slider({
min: 0,
max: 100,
value: 0,
range: false,
animate:"slow",
orientation: "horizontal",
slide: function( event, ui ) {
var hsl_hue = ui.value;
}
});
});
$(function() {
$( "#hsl_saturation_range" ).slider({
min: 0,
max: 100,
value: 0,
range: false,
animate:"slow",
orientation: "horizontal",
slide: function( event, ui ) {
var hsl_saturation = ui.value;
}
});
});
$(function() {
$( "#hsl_light_range" ).slider({
min: 0,
max: 100,
value: 0,
range: false,
animate:"slow",
orientation: "horizontal",
slide: function( event, ui ) {
var hsl_light = ui.value;
}
});
});
I want the solution like this:
the input to converter can be given by the variables. like hsl_hue hsl_saturation hsl_light.
Is there any way to do this?
if no way, what can I do?

New approach (inspired by #Kamil-Kiełczewski solution)
Takes degree, percentage, percentage and returns css hex color:
function hslToHex(h, s, l) {
l /= 100;
const a = s * Math.min(l, 1 - l) / 100;
const f = n => {
const k = (n + h / 30) % 12;
const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
return Math.round(255 * color).toString(16).padStart(2, '0'); // convert to Hex and prefix "0" if needed
};
return `#${f(0)}${f(8)}${f(4)}`;
}
Example:
hslToHex(360, 100, 50) // "#ff0000" -> red
Original version: (still OK, just longer)
Takes degree, percentage, percentage and returns css hex color:
function hslToHex(h, s, l) {
h /= 360;
s /= 100;
l /= 100;
let r, g, b;
if (s === 0) {
r = g = b = l; // achromatic
} else {
const 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;
};
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
const toHex = x => {
const hex = Math.round(x * 255).toString(16);
return hex.length === 1 ? '0' + hex : hex;
};
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
}
Example:
hslToHex(360, 100, 50) // "#ff0000" -> red

Shortest
Try this (wiki, error analysis, more: rgb2hsl, hsv2rgb rgb2hsv and hsl2hsv)
// input: h in [0,360] and s,v in [0,1] - output: r,g,b in [0,1]
function hsl2rgb(h,s,l)
{
let a= s*Math.min(l,1-l);
let f= (n,k=(n+h/30)%12) => l - a*Math.max(Math.min(k-3,9-k,1),-1);
return [f(0),f(8),f(4)];
}
To calc hsl2hex use rgb2hex(...hsl2rgb(30,1,0.5)). To convert string from format e.g. rgb(255, 255, 255) to hex use rgbStrToHex (which handle empty string case) as follows
// oneliner version
let hsl2rgb = (h,s,l, a=s*Math.min(l,1-l), f= (n,k=(n+h/30)%12) => l - a*Math.max(Math.min(k-3,9-k,1),-1)) => [f(0),f(8),f(4)];
// r,g,b are in [0-1], result e.g. #0812fa.
let rgb2hex = (r,g,b) => "#" + [r,g,b].map(x=>Math.round(x*255).toString(16).padStart(2,0) ).join('');
// hexStr e.g #abcdef, result "rgb(171,205,239)"
let hexStr2rgb = (hexStr) => `rgb(${hexStr.substr(1).match(/../g).map(x=>+`0x${x}`)})`;
// rgb - color str e.g."rgb(12,233,43)", result color hex e.g. "#0ce92b"
let rgbStrToHex= rgb=> '#'+rgb.match(/\d+/g).map(x=>(+x).toString(16).padStart(2,0)).join``
console.log(`hsl: (30,0.2,0.3) --> rgb: (${hsl2rgb(30,0.2,0.3)}) --> hex: ${rgb2hex(...hsl2rgb(30,0.2,0.3))}`);
console.log(`rgb: ${hexStr2rgb("#ff647b")} --> hex: ${rgbStrToHex("rgb(255,100, 123)")}`)
// ---------------
// UX
// ---------------
rgb= [0,0,0];
hs= [0,0,0];
let $ = x => document.querySelector(x);
function changeRGB(i,e) {
rgb[i]=e.target.value/255;
hs = rgb2hsl(...rgb);
refresh();
}
function changeHS(i,e) {
hs[i]=e.target.value/(i?255:1);
rgb= hsl2rgb(...hs);
refresh();
}
function refresh() {
rr = rgb.map(x=>x*255|0).join(', ')
hh = rgb2hex(...rgb);
tr = `RGB: ${rr}`
th = `HSL: ${hs.map((x,i)=>i? (x*100).toFixed(2)+'%':x|0).join(', ')}`
thh= `HEX: ${hh}`
$('.box').style.backgroundColor=`rgb(${rr})`;
$('.infoRGB').innerHTML=`${tr}`;
$('.infoHS').innerHTML =`${th}\n${thh}`;
$('#r').value=rgb[0]*255;
$('#g').value=rgb[1]*255;
$('#b').value=rgb[2]*255;
$('#h').value=hs[0];
$('#s').value=hs[1]*255;
$('#l').value=hs[2]*255;
}
function rgb2hsl(r,g,b) {
let a=Math.max(r,g,b), n=a-Math.min(r,g,b), f=(1-Math.abs(a+a-n-1));
let h= n && ((a==r) ? (g-b)/n : ((a==g) ? 2+(b-r)/n : 4+(r-g)/n));
return [60*(h<0?h+6:h), f ? n/f : 0, (a+a-n)/2];
}
refresh();
.box {
width: 50px;
height: 50px;
margin: 20px;
}
body {
display: flex;
}
<div>
<input id="r" type="range" min="0" max="255" oninput="changeRGB(0,event)">R<br>
<input id="g" type="range" min="0" max="255" oninput="changeRGB(1,event)">G<br>
<input id="b" type="range" min="0" max="255" oninput="changeRGB(2,event)">B<br>
<pre class="infoRGB"></pre>
</div>
<div>
<div class="box hsl"></div>
</div>
<div>
<input id="h" type="range" min="0" max="360" oninput="changeHS(0,event)">H<br>
<input id="s" type="range" min="0" max="255" oninput="changeHS(1,event)">S<br>
<input id="l" type="range" min="0" max="255" oninput="changeHS(2,event)">L<br>
<pre class="infoHS"></pre><br>
</div>

HSL to RGB:
/**
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes h, s, and l are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
*
* #param {number} h The hue
* #param {number} s The saturation
* #param {number} l The lightness
* #return {Array} The RGB representation
*/
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)];
}
You can find more information here - HSL to RGB color conversion

Another way to solve this problem is to leverage the window.getComputedStyle capability of modern browsers:
Create an element on the page (it can be hidden, e.g. with display:none, but that appears to suppress output of the opacity / "A" value)
Set a color-valued property of that element using the representation of your choice, e.g. e.style.color = 'hsla(100, 50%, 75%, 0.8)'; (or even named colors like 'rebeccapurple')
Read the value back using window.getComputedStyle(e).color. It will be a string of the form rgb(r,g,b) or rgba(r,g,b,a).
Live demo on CodePen

I've made a small library that can easily convert colors.
This is my HSL to RGB method, which uses a few other utility methods from the library:
Color.hslToRgb = function(hsl, formatted) {
var a, b, g, h, l, p, q, r, ref, s;
if (isString(hsl)) {
if (!hsl.match(Color.HSL_REGEX)) {
return;
}
ref = hsl.match(/hsla?\((.+?)\)/)[1].split(',').map(function(value) {
value.trim();
return parseFloat(value);
}), h = ref[0], s = ref[1], l = ref[2], a = ref[3];
} else if ((isObject(hsl)) && (hasKeys(hsl, ['h', 's', 'l']))) {
h = hsl.h, s = hsl.s, l = hsl.l, a = hsl.a;
} else {
return;
}
h /= 360;
s /= 100;
l /= 100;
if (s === 0) {
r = g = b = l;
} else {
q = l < 0.5 ? l * (1 + s) : l + s - l * s;
p = 2 * l - q;
r = Color.hueToRgb(p, q, h + 1 / 3);
g = Color.hueToRgb(p, q, h);
b = Color.hueToRgb(p, q, h - 1 / 3);
}
return getRgb(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255), a, formatted);
};
If you don't want to use npm, the lib can also be found on GitHub.

I recently had cause to solve this problem and came up with a canvas-based solution. I'm logging it here for posterity only.
In my case I also needed to account for the cumulative effects on the conversion given a range of background colors and a semi-transparent alpha channel...
var HSL2COLOR = function () {
return function (hsl, bg) {
function checkHex(v) {
return 1 === v.length ? '0'+v : v;
}
var data, r, g, b, a,
cnv = document.createElement('canvas'),
ctx = cnv.getContext('2d'),
alpha = /a\(/.test(hsl),
output = {};
return cnv.width = cnv.height = 1,
bg && (ctx.fillStyle = bg, ctx.fillRect(0, 0, 1, 1)),
ctx.fillStyle = hsl,
ctx.fillRect(0, 0, 1, 1),
data = ctx.getImageData(0, 0, 1, 1).data,
r = data[0],
g = data[1],
b = data[2],
a = (data[3] / 255).toFixed(2),
alpha ? (output.hsla = hsl, bg ? output.rgb = 'rgb('+r+','+g+','+b+')' : output.rgba = 'rgb('+r+','+g+','+b+','+a+')') : (output.hsl = hsl, output.rgb = 'rgb('+r+','+g+','+b+')'),
output.hex = '#'+checkHex(r.toString(16))+checkHex(g.toString(16))+checkHex(b.toString(16)),
output;
};
}();
// hsl: no alpha-channel + no background color
console.log(HSL2COLOR('hsl(170, 60%, 45%)'));
/*=> {
hsl: "hsl(170, 60%, 45%)",
rgb: "rgb(45,183,160)",
hex: "#2db7a0"
}
*/
// hsla: alpha-channel + no background color
console.log(HSL2COLOR('hsla(170, 60%, 45%, 0.35)'));
/*=> {
hsla: "hsla(170, 60%, 45%, 0.35)",
rgba: "rgb(42,183,160,0.35)",
hex: "#2ab7a0"
}
*/
// hsla: alpha-channel + background color
console.log(HSL2COLOR('hsla(170, 60%, 45%, 0.35)','#f00'));
/*=> {
hsla: "hsla(170, 60%, 45%, 0.35)",
rgb: "rgb(181,64,56)",
hex: "#b54038"
}
*/
As you can see from the results above, HEX values are not particularly representative when there is an alpha-channel in the input but no background color specified - as the canvas basically sees a transparent background as black. Nonetheless, the rgba value remained coherent.
Anyway, I achieved what I needed to, and perhaps this will be of some use to someone, sometime.
BP

Or you could npm install color-convert and not re-invent the wheel.
const convert = require('color-convert')
const hex = '#' + convert.hsl.hex(96, 48, 59) // convert hsl to hex
const rgb = convert.hsl.rgb(96, 48, 59) // convert hsl to rgb
Life can be simple at times.

from #icl7126 's answer,
you can add another function param to include alpha
function hsl2hex(h,s,l,alpha) {
l /= 100;
const a = s * Math.min(l, 1 - l) / 100;
const f = n => {
const k = (n + h / 30) % 12;
const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
return Math.round(255 * color).toString(16).padStart(2, '0');
// convert to Hex and prefix "0" if needed
};
//alpha conversion
alpha = Math.round(alpha * 255).toString(16).padStart(2, '0');
return `#${f(0)}${f(8)}${f(4)}${alpha}`;
}

Related

Converting between color models

I'm trying to make buttons for converting between color models (hex, rgb, hsl).
I've created a button that generates a random color when clicked on.
And I've created a class that generates the random color in rgb and has methods to convert it to hex or hsl.
I tried creating a "click" event listener on each of the color model buttons but I'm not able to access the newly generated color object that I made through the class (block scope).
Should I have just used normal functions to convert instead of a class constructor?
const colorName = document.querySelector(".colorName");
const genColorBtn = document.querySelector("#genColorBtn");
const hexBtn = document.querySelector("#hexBtn");
const rgbBtn = document.querySelector("#rgbBtn");
const hslBtn = document.querySelector("#hslBtn");
// Generate Random Color
const genRandomColor = () => {
const r = Math.floor(Math.random() * 255 + 1);
const g = Math.floor(Math.random() * 255 + 1);
const b = Math.floor(Math.random() * 255 + 1);
return new Color(r, g, b);
};
genColorBtn.addEventListener("click", function () {
const newColor = genRandomColor();
document.body.style.backgroundColor = newColor.rgb();
colorName.innerText = newColor.rgb();
});
// Color Model Conversions
class Color {
constructor(r, g, b) {
this.r = r;
this.g = g;
this.b = b;
this.calcHSL();
}
// Add methods to the prototype.
hex() {
const { r, g, b } = this;
return (
"#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)
);
}
rgb() {
const { r, g, b } = this;
return `rgb(${r}, ${g}, ${b})`;
}
hsl() {
const { h, s, l } = this;
return `hsl(${h}, ${s}%, ${l}%)`;
}
// For hsl
calcHSL() {
let { r, g, b } = this;
// Make r, g, and b fractions of 1
r /= 255;
g /= 255;
b /= 255;
// Find greatest and smallest channel values
let cmin = Math.min(r, g, b),
cmax = Math.max(r, g, b),
delta = cmax - cmin,
h = 0,
s = 0,
l = 0;
if (delta == 0) h = 0;
else if (cmax == r)
// Red is max
h = ((g - b) / delta) % 6;
else if (cmax == g)
// Green is max
h = (b - r) / delta + 2;
// Blue is max
else h = (r - g) / delta + 4;
h = Math.round(h * 60);
// Make negative hues positive behind 360°
if (h < 0) h += 360;
// Calculate lightness
l = (cmax + cmin) / 2;
// Calculate saturation
s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
// Multiply l and s by 100
s = +(s * 100).toFixed(1);
l = +(l * 100).toFixed(1);
// Assign h, s, l to the object instance so that we can reuse it.
this.h = h;
this.s = s;
this.l = l;
}
}
<body>
<main>
<h1>Random Color Flipper</h1>
<h2>Color: <span class="colorName">rgb(255, 255, 255)</span></h2>
<div class="colorModes">
<button id="hexBtn">hex</button>
<button id="rgbBtn">rgb</button>
<button id="hslBtn">hsl</button>
</div>
<button id="genColorBtn">Generate Color</button>
</main>
<script src="app.js"></script>
</body>
Your code seems fine, but your hex, rgb and hsl buttons were not wired up. I added a variable curRGB to store the current RGB values (though there is probably a more elegant getter/setter for your Class that could be written). I did take a stab at the hexBtn listener:
hexBtn.addEventListener("click", function () {
colorName.innerText = new Color(...curRGB).hex();
});
const colorName = document.querySelector(".colorName");
const genColorBtn = document.querySelector("#genColorBtn");
const hexBtn = document.querySelector("#hexBtn");
const rgbBtn = document.querySelector("#rgbBtn");
const hslBtn = document.querySelector("#hslBtn");
let curRGB
// Generate Random Color
const genRandomColor = () => {
const r = Math.floor(Math.random() * 255 + 1);
const g = Math.floor(Math.random() * 255 + 1);
const b = Math.floor(Math.random() * 255 + 1);
curRGB = [r, g, b]
return new Color(r, g, b);
};
genColorBtn.addEventListener("click", function () {
const newColor = genRandomColor();
document.body.style.backgroundColor = newColor.rgb();
colorName.innerText = newColor.rgb();
});
hexBtn.addEventListener("click", function () {
colorName.innerText = new Color(...curRGB).hex();
});
// Color Model Conversions
class Color {
constructor(r, g, b) {
this.r = r;
this.g = g;
this.b = b;
this.calcHSL();
}
// Add methods to the prototype.
hex() {
const { r, g, b } = this;
return (
"#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)
);
}
rgb() {
const { r, g, b } = this;
return `rgb(${r}, ${g}, ${b})`;
}
hsl() {
const { h, s, l } = this;
return `hsl(${h}, ${s}%, ${l}%)`;
}
// For hsl
calcHSL() {
let { r, g, b } = this;
// Make r, g, and b fractions of 1
r /= 255;
g /= 255;
b /= 255;
// Find greatest and smallest channel values
let cmin = Math.min(r, g, b),
cmax = Math.max(r, g, b),
delta = cmax - cmin,
h = 0,
s = 0,
l = 0;
if (delta == 0) h = 0;
else if (cmax == r)
// Red is max
h = ((g - b) / delta) % 6;
else if (cmax == g)
// Green is max
h = (b - r) / delta + 2;
// Blue is max
else h = (r - g) / delta + 4;
h = Math.round(h * 60);
// Make negative hues positive behind 360°
if (h < 0) h += 360;
// Calculate lightness
l = (cmax + cmin) / 2;
// Calculate saturation
s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
// Multiply l and s by 100
s = +(s * 100).toFixed(1);
l = +(l * 100).toFixed(1);
// Assign h, s, l to the object instance so that we can reuse it.
this.h = h;
this.s = s;
this.l = l;
}
}
<main>
<h1>Random Color Flipper</h1>
<h2>Color: <span class="colorName">rgb(255, 255, 255)</span></h2>
<div class="colorModes">
<button id="hexBtn">hex</button>
<button id="rgbBtn">rgb</button>
<button id="hslBtn">hsl</button>
</div>
<button id="genColorBtn">Generate Color</button>
</main>

How to append the hex color code

Im using jquery-ui to make a color picker and im almost done with it.
But i was wondering how i could append the hex color code.
HTML
<div id="slider-horizontal"></div>
<div class="pin"></div>
JQUERY
$(function() {
var box = $('.pin')[0];
$("#slider-horizontal").slider({
orientation: "horizontal",
min: 0,
max: 360,
value: 0,
slide: function(event, ui) {
box.style.background = 'hsl(' + ui.value + ', 100%, 50%)';
}
});
});
Any guidance would be appreciated. Thanks very much.
This page has many functions for converting from HSL to RGB. Using this function, you could have a div that has the RGB value in it, updated with the slide event along with the background color. Here is a snippet of the function:
/**
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes h, s, and l are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
*
* #param Number h The hue
* #param Number s The saturation
* #param Number l The lightness
* #return Array The RGB representation
*/
function hslToRgb(h, s, l){
var r, g, b;
if(s == 0){
r = g = b = l; // achromatic
}else{
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 [r * 255, g * 255, b * 255];
}
Going from RGB to HEX is much simpler, and can be achieved with the following functions from this SO answer:
function componentToHex(c) {
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
function rgbToHex(r, g, b) {
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}

Convert a 0-1 value to a hex colour?

I'm creating an app that visualises stars using a NASA API. The colour of a star comes back as a 0 to 1 value, with 0 being pure blue, and 1 being pure red. Essentially I need to set up a way to convert 0-1 values in javascript to a sliding HEX (or rgb) scale, progressing like this:
0: blue (9aafff)
.165: blue white (cad8ff)
.33: white (f7f7ff)
.495: yellow white (fcffd4)
.66: yellow (fff3a1)
.825: orange (ffa350)
1: red (fb6252)
Is this possible? I don't have any idea how to even begin to approach this. Cheers!
The best would be to work in another color space than the RGB one. For example HSL.
Example:
var stones = [ // Your Data
{v:0, hex:'#9aafff'},
{v:.165, hex:'#cad8ff'},
{v:.33, hex:'#f7f7ff'},
{v:.495, hex:'#fcffd4'},
{v:.66, hex:'#fff3a1'},
{v:.825, hex:'#ffa350'},
{v:1, hex:'#fb6252'},
]
stones.forEach(function(s){
s.rgb = hexToRgb(s.hex);
s.hsl = rgbToHsl.apply(0, s.rgb);
});
function valueToRgbColor(val){
for (var i=1; i<stones.length; i++) {
if (val<=stones[i].v) {
var k = (val-stones[i-1].v)/(stones[i].v-stones[i-1].v),
hsl = interpolArrays(stones[i-1].hsl, stones[i].hsl, k);
return 'rgb('+hslToRgb.apply(0,hsl).map(function(v){ return v|0})+')';
}
}
throw "bad value";
}
/**
* Converts an RGB color value to HSL. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes r, g, and b are contained in the set [0, 255] and
* returns h, s, and l in the set [0, 1].
*
* #param Number r The red color value
* #param Number g The green color value
* #param Number b The blue color value
* #return Array The HSL representation
*/
function rgbToHsl(r, g, b){
r /= 255, g /= 255, b /= 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 [h, s, l];
}
/**
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes h, s, and l are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
*
* #param Number h The hue
* #param Number s The saturation
* #param Number l The lightness
* #return Array The RGB representation
*/
function hslToRgb(h, s, l){
var r, g, b;
if(s == 0){
r = g = b = l; // achromatic
}else{
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 [r * 255, g * 255, b * 255];
}
function hexToRgb(hex) {
return /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
.slice(1).map(function(v){ return parseInt(v,16) });
}
function interpolArrays(a,b,k){
var c = a.slice();
for (var i=0;i<a.length;i++) c[i]+=(b[i]-a[i])*k;
return c;
}
var stones = [ // Your Data
{v:0, hex:'#9aafff'},
{v:.165, hex:'#cad8ff'},
{v:.33, hex:'#f7f7ff'},
{v:.495, hex:'#fcffd4'},
{v:.66, hex:'#fff3a1'},
{v:.825, hex:'#ffa350'},
{v:1, hex:'#fb6252'},
]
stones.forEach(function(s){
s.rgb = hexToRgb(s.hex);
s.hsl = rgbToHsl.apply(0, s.rgb);
});
function valueToRgbColor(val){
for (var i=1; i<stones.length; i++) {
if (val<=stones[i].v) {
var k = (val-stones[i-1].v)/(stones[i].v-stones[i-1].v),
hsl = interpolArrays(stones[i-1].hsl, stones[i].hsl, k);
return 'rgb('+hslToRgb.apply(0,hsl).map(function(v){ return v|0})+')';
}
}
throw "bad value";
}
for (var i=0; i<=1; i+=.03) {
var color = valueToRgbColor(i);
$('<div>').css({background:color}).text(i.toFixed(2)+" -> "+color).appendTo('body');
}
body {
background: #222;
}
div {
width:200px;
margin:auto;
color: #333;
padding: 2px;
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
For this example, I took the color space conversion functions here but there are easy to find once you know what to look for.
Note that modern browsers understand HSL colors (exemple: background: hsl(120,100%, 50%);) so, if you're just building HTML, you don't have to embed all this code in your page, just precompute the color stops and interpolate on the HSL values directly.
Here is one the solution in pure Javascript I just did. It process a linear interpolation between two colors.
/*
NASA color to RGB function
by Alexis Paques
It process a linear interpolation between two colors, here is the scheme:
0: blue
.165: blue white
.33: white
.495: yellow white
.66: yellow
.825: orange
1: red
*/
var blue = [0,0,255];
var bluewhite = [127,127,255];
var white = [255,255,255];
var yellowwhite = [255,255,127];
var yellow = [255,255,0];
var orange = [255,127,0];
var red = [255,0,0];
function color01toRGB(color01){
var RGB = [0,0,0];
var fromRGB = [0,0,0];
var toRGB = [0,0,0];
if(!color01)
return '#000000';
if(color01 > 1 || color01 < 0)
return '#000000';
if(color01 >= 0 && color01 <= 0.165 ){
fromRGB = blue;
toRGB = bluewhite;
}
else if(color01 > 0.165 && color01 <= 0.33 ){
fromRGB = bluewhite;
toRGB = white;
}
else if(color01 > 0.33 && color01 <= 0.495 ){
fromRGB = white;
toRGB = yellowwhite;
}
else if(color01 > 0.495 && color01 <= 0.66 ){
fromRGB = yellowwhite;
toRGB = yellow;
}
else if(color01 > 0.66 && color01 <= 0.825 ){
fromRGB = yellow;
toRGB = orange;
}
else if(color01 > 0.825 && color01 <= 1 ){
fromRGB = orange;
toRGB = red;
}
// 0.165
for (var i = RGB.length - 1; i >= 0; i--) {
RGB[i] = Math.round(fromRGB[i]*color01/0.165 + toRGB[i]*(1-color01/0.165)).toString(16);
};
return '#' + RGB.join('');
}
Since you have a list of values, all pretty well-saturated and bright, you can probably interpolate in the current (RGB) space for this. It won't be quite as pretty as if you convert to HSL, but will work fine for the colors you have.
Since you don't have any weighting or curves in the data, going with a simple linear interpolation should work just fine. Something like:
var stops = [
[0, 154, 175, 255],
[0.165, 202, 216, 255],
[0.33, 247, 247, 255],
[0.495, 252, 255, 212],
[0.66, 255, 243, 161],
[0.825, 255, 163, 80],
[1, 251, 98, 82]
];
function convertColor(color) {
var c = Math.min(Math.max(color, 0), 1); // Clamp between 0 and 1
// Find the first stop below c
var startIndex = 0;
for (; stops[startIndex][0] < c && startIndex < stops.length; ++startIndex) {
// nop
}
var start = stops[startIndex];
console.log('using stop', startIndex, 'as start');
// Find the next stop (above c)
var stopIndex = startIndex + 1;
if (stopIndex >= stops.length) {
stopIndex = stops.length - 1;
}
var stop = stops[stopIndex];
console.log('using stop', stopIndex, 'as stop');
// Find the distance from start to c and start to stop
var range = stop[0] - start[0];
var diff = c - start[0];
// Convert diff into a ratio from start to stop
if (range > 0) {
diff /= range;
}
console.log('interpolating', c, 'between', stop[0], 'and', start[0], 'by', diff);
// Convert from RGB to HSL
var a = rgbToHsl(start[1], start[2], start[3]);
var b = rgbToHsl(stop[1], stop[2], stop[3]);
console.log('hsl stops', a, b);
// Interpolate between the two colors (start * diff + (stop * (1 - diff)))
var out = [0, 0, 0];
out[0] = a[0] * diff + (b[0] * (1 - diff));
out[1] = a[1] * diff + (b[1] * (1 - diff));
out[2] = a[2] * diff + (b[2] * (1 - diff));
console.log('interpolated', out);
// Convert back from HSL to RGB
var r = hslToRgb(out[0], out[1], out[2]);
r = r.map(function(rv) {
// Round each component of the output
return Math.round(rv);
});
return r;
}
// Set the divs
var divs = document.querySelectorAll('.star');
Array.prototype.forEach.call(divs, function(star) {
var color = convertColor(star.dataset.color);
var colorStr = 'rgb(' + color[0] + ',' + color[1] + ',' + color[2] + ')';
console.log('setting', star, 'to', colorStr);
star.style.backgroundColor = colorStr;
});
// HSL to RGB conversion from http://stackoverflow.com/a/30758827/129032
function rgbToHsl(r, g, b) {
r /= 255, g /= 255, b /= 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 [h, s, l];
}
function hslToRgb(h, s, l) {
var r, g, b;
if (s == 0) {
r = g = b = l; // achromatic
} else {
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 [r * 255, g * 255, b * 255];
}
.star {
width: 24px;
height: 24px;
display: inline-block;
box-shadow: 0px 0px 16px -2px rgba(0, 0, 0, 0.66);
}
<div class="star" data-color="0.0"></div>
<div class="star" data-color="0.05"></div>
<div class="star" data-color="0.1"></div>
<div class="star" data-color="0.15"></div>
<div class="star" data-color="0.2"></div>
<div class="star" data-color="0.25"></div>
<div class="star" data-color="0.3"></div>
<div class="star" data-color="0.35"></div>
<div class="star" data-color="0.4"></div>
<div class="star" data-color="0.45"></div>
<div class="star" data-color="0.5"></div>
<div class="star" data-color="0.55"></div>
<div class="star" data-color="0.6"></div>
<div class="star" data-color="0.65"></div>
<div class="star" data-color="0.7"></div>
<div class="star" data-color="0.75"></div>
<div class="star" data-color="0.8"></div>
<div class="star" data-color="0.85"></div>
<div class="star" data-color="0.9"></div>
<div class="star" data-color="0.95"></div>
<div class="star" data-color="1.0"></div>

Changing pixel in canvas imageData to hsl(60, 100%, 50%)

I would like to change pixels of an HTML5 canvas to an hsl value. It could be any hsl value that is chosen by the user.
I can get the canvas imageData with var imageData = canvas.getImageData(0, 0, 200, 200);
But the imageData.data array contains values in rgba. Actually each value in the array is a byte so -
data[0] = r, data[1] = b, data[2] = g, data[3] = a, data[4] = r, data[5] = b, data[6] = g, data[7] = a etc.
Is there an api that can be used to manipulate imageData? An api that would abstract the raw data so that - data[0] = rgba, data[1] = rgba etc?
And that might have methods like - data[0].setValueHSL(60, 100%, 50%);
If this api does not exist is there a class that can create/represent an hsl value and which can convert the value to rgb?
I am not sure if you are still looking for the answer since this was asked a long time ago. But I was trying to do the same and encountered the answer on Why doesn't this Javascript RGB to HSL code work?, this should do the trick :
function rgbToHsl(r, g, b) {
r /= 255, g /= 255, b /= 255;
var max = Math.max(r, g, b);
var 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 'hsl(' + Math.floor(h * 360) + ',' + Math.floor(s * 100) + '%,' + Math.floor(l * 100) + '%)';
}
You could write one its as simple as this
parseImageData = function(imageData) {
var newImageData = [];
for ( var i = imageData - 1; i>0; i-4) {
newImageData.push([ imageData[i],
imageData[i-1],
imageData[i-2],
imageData[i-3] ]);
}
return newImageData;
}
then if you want to convert it back
parseNewImageData = function ( newImageData ) {
var imageData = [];
for ( var i = newImageData - 1; i>=0; --i) {
imageData.push( imageData[i][0] );
imageData.push( imageData[i][1] );
imageData.push( imageData[i][2] );
imageData.push( imageData[i][3] );
}
return imageData;
}
super easy and you can make it do specifically what you need it to!
I hope this helps!

Generating random color deviations

I want to take a color and generate random deviations from that color with JavaScript. For example, say I have a color #33CCFF, I want to feed that number into the script and get numbers like #8AE2FF, #00AAE2, and #7BDEFF. Basically, the hue should stay the same, but the saturation/brightness should fluctuate a bit.
What's the fastest and simplest way to generate these numbers?
Convert your RGB color to HSL, keep the Hue, derive Saturation and Lightness(Brightness) with a small random number, for instance, then convert back to RGB.
For converting: http://en.wikipedia.org/wiki/HSL_and_HSV#From_HSL
Bonus, the code for conversion: http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
Honestly I don't know how Hex codes work out saturation/brightness, so I would convert to HSL:
http://serennu.com/colour/rgbtohsl.php
change up the ones you want, and change it back. The only major drawback I see is the possibility that the HSL doesn't exist in the Hex field after you've changed it.
As in,
HSL colour space has only 360 x 100 x 100 = 3,600,000
colours in it whilst the RGB colour space has 256 x 256 x 256 =
16,77,216 colours in it, That's four half times as many.
I know this question is one year old, but I was looking around for a solution and couldn't find it.
But basing on suggestions here and there (mainly on SO) I created ready to use script.
NOTICE 1: colorToRGB function is unreliable. I found it in SO (source attached). I changed hex values for some colors but I didn't want to check it for all of them. For best results always use hex value.
NOTICE 2: deviation in generateSimilarColor function is set just for my purposes. You can change it for better results.
NOTICE 3: Many functions are found on SO. I've just gathered them together and I'm providing links to corresponding questions.
JavaScript code below (testing environment on the bottom of the post):
/**
* Generates similar color based on passed one
*
* #param color accepts few formats, e.g. white, #ffffff or [255, 255, 255]
* #return hex value, e.g. #ffffff
*
* #author Carlos (StackOverflow)
*/
function generateSimilarColor(color) {
var rand = generateRandomInt(-5, 5);
if(rand < 0) rand -= 5;
else rand += 5;
var deviation = rand / 100;
if(!isArray(color) || color.length != 3) { // this is not [r, g, b] array
var hexNotation = colorToRGB(color);
if(!hexNotation) { // we couldn't find HEX value of this string based on color name
if(!isString(color) || color.length != 7 || color[0] != '#') { // this is not string in this format: #ffffff
return false;
}
else {
hexNotation = color;
}
}
color = hexToRgb(hexNotation);
if(!color) {
return false;
}
}
var hsl = rgbToHsl(color[0], color[1], color[2]);
// saturation
hsl[1] += deviation;
if(hsl[1] > 1) hsl[1] = 1 - Math.abs(deviation);
else if(hsl[1] < 0) hsl[1] = Math.abs(deviation);
// lightness
hsl[2] += deviation;
if(hsl[2] > 1) hsl[2] = 1 - Math.abs(deviation);
else if(hsl[2] < 0) hsl[2] = Math.abs(deviation);
color = hslToRgb(hsl[0], hsl[1], hsl[2]);
var hex = rgbToHex(color[0], color[1], color[2]);
return hex;
}
/**
* Returns a random integer between min and max
* Using Math.round() will give you a non-uniform distribution!
*
* #source http://stackoverflow.com/questions/1527803/generating-random-numbers-in-javascript-in-a-specific-range
*/
function generateRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
/**
* #source: [slightly changed] http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb
*/
function componentToHex(c) {
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
/**
* #source: [slightly changed] http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb
*/
function rgbToHex(r, g, b) {
r = parseInt(r);
g = parseInt(g);
b = parseInt(b);
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}
/**
* #source http://stackoverflow.com/questions/1058427/how-to-detect-if-a-variable-is-an-array
*/
function isArray(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
}
/**
* #source http://stackoverflow.com/questions/4059147/check-if-a-variable-is-a-string
*/
function isString(str) {
return (typeof str == 'string' || str instanceof String);
}
/**
* #source: http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb slightly change
*/
function hexToRgb(hex) {
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, function(m, r, g, b) {
return r + r + g + g + b + b;
});
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? [
parseInt(result[1], 16),
parseInt(result[2], 16),
parseInt(result[3], 16)
] : false;
}
/**
* Converts an RGB color value to HSL. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes r, g, and b are contained in the set [0, 255] and
* returns h, s, and l in the set [0, 1].
*
* #param Number r The red color value
* #param Number g The green color value
* #param Number b The blue color value
* #return Array The HSL representation
*
* #source http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
*/
function rgbToHsl(r, g, b) {
r /= 255, g /= 255, b /= 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 [h, s, l];
}
/**
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes h, s, and l are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
*
* #param Number h The hue
* #param Number s The saturation
* #param Number l The lightness
* #return Array The RGB representation
*/
function hslToRgb(h, s, l) {
var r, g, b;
if(s == 0){
r = g = b = l; // achromatic
}else{
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 [r * 255, g * 255, b * 255];
}
/**
* #source [slightly changed] http://stackoverflow.com/questions/1573053/javascript-function-to-convert-color-names-to-hex-codes
*/
function colorToRGB(color) {
var colors = {"aliceblue":"#f0f8ff","antiquewhite":"#faebd7","aqua":"#00ffff","aquamarine":"#7fffd4","azure":"#f0ffff",
"beige":"#f5f5dc","bisque":"#ffe4c4","black":"#000000","blanchedalmond":"#ffebcd","blue":"#0000ff","blueviolet":"#8a2be2","brown":"#603311","burlywood":"#deb887",
"cadetblue":"#5f9ea0","chartreuse":"#7fff00","chocolate":"#d2691e","coral":"#ff7f50","cornflowerblue":"#6495ed","cornsilk":"#fff8dc","crimson":"#dc143c","cyan":"#00ffff",
"darkblue":"#00008b","darkcyan":"#008b8b","darkgoldenrod":"#b8860b","darkgray":"#a9a9a9","darkgreen":"#006400","darkkhaki":"#bdb76b","darkmagenta":"#8b008b","darkolivegreen":"#556b2f",
"darkorange":"#ff8c00","darkorchid":"#9932cc","darkred":"#8b0000","darksalmon":"#e9967a","darkseagreen":"#8fbc8f","darkslateblue":"#483d8b","darkslategray":"#2f4f4f","darkturquoise":"#00ced1",
"darkviolet":"#9400d3","deeppink":"#ff1493","deepskyblue":"#00bfff","dimgray":"#696969","dodgerblue":"#1e90ff",
"firebrick":"#b22222","floralwhite":"#fffaf0","forestgreen":"#228b22","fuchsia":"#ff00ff",
"gainsboro":"#dcdcdc","ghostwhite":"#f8f8ff","gold":"#ffd700","goldenrod":"#daa520","gray":"#808080","green":"#008000","greenyellow":"#adff2f",
"honeydew":"#f0fff0","hotpink":"#ff69b4",
"indianred ":"#cd5c5c","indigo ":"#4b0082","ivory":"#fffff0","khaki":"#f0e68c",
"lavender":"#e6e6fa","lavenderblush":"#fff0f5","lawngreen":"#7cfc00","lemonchiffon":"#fffacd","lightblue":"#add8e6","lightcoral":"#f08080","lightcyan":"#e0ffff","lightgoldenrodyellow":"#fafad2",
"lightgrey":"#d3d3d3","lightgreen":"#90ee90","lightpink":"#ffb6c1","lightsalmon":"#ffa07a","lightseagreen":"#20b2aa","lightskyblue":"#87cefa","lightslategray":"#778899","lightsteelblue":"#b0c4de",
"lightyellow":"#ffffe0","lime":"#00ff00","limegreen":"#32cd32","linen":"#faf0e6",
"magenta":"#ff00ff","maroon":"#800000","mediumaquamarine":"#66cdaa","mediumblue":"#0000cd","mediumorchid":"#ba55d3","mediumpurple":"#9370d8","mediumseagreen":"#3cb371","mediumslateblue":"#7b68ee",
"mediumspringgreen":"#00fa9a","mediumturquoise":"#48d1cc","mediumvioletred":"#c71585","midnightblue":"#191970","mintcream":"#f5fffa","mistyrose":"#ffe4e1","moccasin":"#ffe4b5",
"navajowhite":"#ffdead","navy":"#000080",
"oldlace":"#fdf5e6","olive":"#808000","olivedrab":"#6b8e23","orange":"#ffa500","orangered":"#ff4500","orchid":"#da70d6",
"palegoldenrod":"#eee8aa","palegreen":"#98fb98","paleturquoise":"#afeeee","palevioletred":"#d87093","papayawhip":"#ffefd5","peachpuff":"#ffdab9","peru":"#cd853f","pink":"#ffc0cb","plum":"#dda0dd","powderblue":"#b0e0e6","purple":"#800080",
"red":"#ff0000","rosybrown":"#bc8f8f","royalblue":"#4169e1",
"saddlebrown":"#8b4513","salmon":"#fa8072","sandybrown":"#f4a460","seagreen":"#2e8b57","seashell":"#fff5ee","sienna":"#a0522d","silver":"#c0c0c0","skyblue":"#87ceeb","slateblue":"#6a5acd","slategray":"#708090","snow":"#fffafa","springgreen":"#00ff7f","steelblue":"#4682b4",
"tan":"#d2b48c","teal":"#008080","thistle":"#d8bfd8","tomato":"#ff6347","turquoise":"#40e0d0",
"violet":"#8F00FF",
"wheat":"#f5deb3","white":"#ffffff","whitesmoke":"#f5f5f5",
"yellow":"#ffff00","yellowgreen":"#9acd32"};
if (typeof colors[color.toLowerCase()] != 'undefined')
return colors[color.toLowerCase()];
return false;
}
Testing environment (with jQuery required):
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<title>Color Test</title>
<script type="text/javascript" src="js/libs/jquery.js"></script>
<script type="text/javascript" src="js/color.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var i, j, colors = ['blue', 'orange', 'green', 'red', 'yellow', 'brown', 'violet'];
for(i = 0; i < colors.length; i++) {
for(j = 0; j < 3; j++) {
$("body").append('<div style="height: 20px; background-color: ' + generateSimilarColor(colors[i]) + ';"></div>');
}
$("body").append('<div style="height: 20px;"></div>');
}
});
</script>
</head>
<body></body>
</html>
Any bugs or suggestions - please write.

Categories

Resources