Javascript converting hex values automatically [duplicate] - javascript

This question already has answers here:
How to get hex color value rather than RGB value?
(24 answers)
Closed 9 years ago.
Currently I am heaving problems using this really simple bit of code: field.style.backgroundColor="#ffffff"; Till now it has worked fine for me, but just recently I noticed that in webkit the hex values is always converted to rgb values like rgb(255, 255, 255). Usually that wouldn't be a problem, but I need the hex values later on in a php script creating an image. And converting them back just slows down the whole process. So my really basic question is how can I avoid getting those annoying rgb values.

You can split the value into it's parts, convert each number to hexidecimal, then return a formatted string, e.g.:
// convert rgb colour values to hex, e.g. rgb(255, 255, 255) to #ffffff;
function rgbToHex(rgb) {
var re = /^rgb\(.*\)$/;
var bits;
function z(n){return (n<10?'0':'') + n;}
if (re.test(rgb)) {
bits = rgb.match(/\d+/g);
return '#' + z((+bits[0]).toString(16)) +
z((+bits[1]).toString(16)) +
z((+bits[2]).toString(16));
}
return rgb;
}
alert(rgbToHex('rgb(255, 255, 255)')); // #ffffff
alert(rgbToHex('rgb(0, 0, 0)')); // #000000
alert(rgbToHex('rgb(100, 230, 90)')); // #64e65a
You might need an i flag on the test regular expression in case some browsers return "RGB(…)".
Edit
Based on Xotic750's post, the z function should be:
function z(n){return (n.length == 1?'0':'') + n;}
A less strict regular expression may suit too:
var re = /^rgb/i;
The other fails are a case of garbage in, garbage out. The regular expression can be modified to allow a space in "rgb (" if required. An updated version is:
function rgbToHex(rgb) {
var re = /^rgb/i;
var bits = rgb.match(/\d+/g);;
function z(n) {
return (n.length == 1? '0' : '') + n;
}
if (re.test(rgb) && bits.length == 3) {
return '#' + z((+bits[0]).toString(16))
+ z((+bits[1]).toString(16))
+ z((+bits[2]).toString(16));
}
return rgb;
}
The only choice left is if the test fails, should it return the original string or undefined?

It is actually fairly trivial to write your own converters, and it is a really fast calculation, by why reinvent the wheel when there is one with a pneumatic tyre already fitted? :) Have a look at colours javascript library.
Ok, so the library wasn't your thing, here is a function to do it for you.
var a = "rgb(10, 128, 255)";
var b = "rgb( 10, 128, 255)";
var c = "rgb(10, 128, 255 )";
var d = "rgb ( 10, 128, 255 )";
var e = "RGB ( 10, 128, 255 )";
var f = "rgb(10,128,255)";
var g = "rgb(10, 128,)";
var rgbToHex = (function () {
var rx = /^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i;
function pad(num) {
if (num.length === 1) {
num = "0" + num;
}
return num;
}
return function (rgb, uppercase) {
var rxArray = rgb.match(rx),
hex;
if (rxArray !== null) {
hex = pad(parseInt(rxArray[1], 10).toString(16)) + pad(parseInt(rxArray[2], 10).toString(16)) + pad(parseInt(rxArray[3], 10).toString(16));
if (uppercase === true) {
hex = hex.toUpperCase();
}
return hex;
}
return;
};
}());
console.log(rgbToHex(a));
console.log(rgbToHex(b, true));
console.log(rgbToHex(c));
console.log(rgbToHex(d));
console.log(rgbToHex(e));
console.log(rgbToHex(f));
console.log(rgbToHex(g));
On jsfiddle
And here are the jsperfs

Can't you just interpret the value as a hex value in php without even the need of converting it?
Actually i don't know what JavaScript hands you there exactly but if it is just single value interpret them in hex and cocatenate them.
There shouldn't be a loss of performance.
Greetings

Related

How can I find the average of N colors?

I'm looking for a way to find the average of an unspecified number of colors. I spent a lot of time looking for a way to do this. First I tried converting my colors to CMYK and averaging them, but that didn't provide the result I expected. Then I saw that in several different places, converting the colors to CIE L*a*b* space and then averaging is the preferred way of doing this. So I looked up how to convert RGB colors to LAB space and converted into Javascript the necessary algorithms to make this happen.
Now that I have my colors in LAB space, I thought it would be as simple as finding the average of my colors, so I wrote this function to do the trick:
color.mixRGB = function() {
var cols = Array.prototype.slice.call(arguments),
i = cols.length,
lab = {l: 0, a: 0, b: 0};
while(i--) {
if (typeof cols[i].r === "undefined" && typeof cols[i].g === "undefined" && typeof cols[i] === "undefined") {
console.log("Not enough parameters supplied for color " + i + ".");
return;
}
if(cols[i].r === 0 && cols[i].g === 0 && cols[i].b === 0) {
cols.splice(i, 1);
} else {
cols[i] = color.RGBtoLAB(cols[i]);
lab.l += cols[i].l;
lab.a += cols[i].a;
lab.b += cols[i].b;
}
}
lab.l /= cols.length;
lab.a /= cols.length;
lab.b /= cols.length;
return color.LABtoRGB(lab);
};
If I enter RGB (255, 0, 0) and RGB(0, 0, 255) into the function, I get RGB(202, -59, 136). This color is nothing near what Color Hexa says is the average of those two RGBs, which is RGB (128, 0, 128), a.k.a purple.
I went back over all my code, and so far I've managed to determine that the problem does not lie with any of my conversion algorithms by double- and triple-checking them against Color Hexa and EasyRGB. That means either a) the issue must lie with how I'm averaging the colors or b) I've been misinformed and I shouldn't attempt to mix colors in CIE L*a*b* space.
What exactly am I missing here? Using my current algorithm, why is averaging RGB (255, 0, 0) and RGB (0, 0, 255) not giving me the same results that Color Hexa (or even visual estimation) provides? (here's a fiddle of my problem)
Lets say you have your colors defined by R0, G0, B0 and R1, G1, B1. Then blended/average color will have following RGB values:
RA = (R0+R1)/2;
GA = (G0+G1)/2;
BA = (B0+B1)/2;
Thats it, basically.
A null return means there has been an error.
color.mixRGB = function() {
var cols = Array.prototype.slice.call(arguments),
i = cols.length,
rTotal = 0, gTotal = 0, rTotal = 0, colTotal = 0;
while(i--) {
// NOTE: you had && in your code, I think they should be ||
if (typeof cols[i].r === "undefined" || typeof cols[i].g === "undefined" || typeof cols[i] === "undefined") {
console.log("Not enough parameters supplied for color " + i + ".");
return null;
}
colTotal++;
rTotal += cols[i].r;
gTotal += cols[i].g;
bTotal += cols[i].b;
}
if(colTotal === 0) return null;
// I am not sure what you are trying to return, just build it up with your rgb values
return (new color(Math.round(rTotal / colTotal), Math.round(gTotal / colTotal), Math.round(bTotal / colTotal)));
};

Converting RGB to HEX fails

Following this question and many others, I'm trying to convert an rgb value to a hex value.
Copying/pasting the most used and accepted answer, I've made this script
function componentToHex(c) {
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
function rgbToHex(rgb) {
var colors = rgb.split("(")
colors = colors[1].split(")")
colors = colors[0].split(",")
var r = 255 - parseInt(colors[0])
var g = 255 - parseInt(colors[1])
var b = 255 - parseInt(colors[2])
return componentToHex(r) + componentToHex(g) + componentToHex(b);
}
alert(rgbToHex("rgb(0, 51, 255)"))
Result:
ffcc00
Expected result:
0033ff
Why is it not working?
Use the parseInt result directly, not 255 minus the value:
var r = parseInt(colors[0])
var g = parseInt(colors[1])
var b = parseInt(colors[2])
You're currently producing the exact opposite color by outputting values complementary to (rather than equivalent to) the input values.
Instead of 255 - parseInt(colors[i]), it should be parseInt(colors[i]).
In your current implementation - if red is 0, 255 - 0 = 255, which in hex is FF.

Using Native JavaScript to Desaturate a Colour

I have a colour picker where the user can specify a hex colour.
I also want a saturation slider where the user can adjust the saturation, and get the new hex colour as an output.
Is there a way I can, in JavaScript, convert a saturation value, and a hex colour, into a new hex colour?
So, say for example I have a value #FF0000 and a saturation of 50 (out of 100) how would I ascertain the new hex colour from this?
I can't use any libraries for it because I'm creating it as a plugin for my website, and I'm trying to keep it as light as possible.
http://jsfiddle.net/5sfDQ/
$("#color, #saturation").change(function(){
updateColor();
});
function updateColor(){
var col = hexToRgb($("#color").val());
var sat = Number($('#saturation').val())/100;
var gray = col.r * 0.3086 + col.g * 0.6094 + col.b * 0.0820;
col.r = Math.round(col.r * sat + gray * (1-sat));
col.g = Math.round(col.g * sat + gray * (1-sat));
col.b = Math.round(col.b * sat + gray * (1-sat));
var out = rgbToHex(col.r,col.g,col.b);
$('#output').val(out);
$('body').css("background",out);
}
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);
}
function hexToRgb(hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}
function applySat(sat, hex) {
var hash = hex.substring(0, 1) === "#";
hex = (hash ? hex.substring(1) : hex).split("");
var long = hex.length > 3,
rgb = [],
i = 0,
len = 3;
rgb.push( hex.shift() + (long ? hex.shift() : "") );
rgb.push( hex.shift() + (long ? hex.shift() : "") );
rgb.push( hex.shift() + (long ? hex.shift() : "") );
for( ; i < len; i++ ) {
if ( !long ) {
rgb[i] += rgb[i];
}
rgb[i] = Math.round( parseInt(rgb[i], 16)/100*sat).toString(16);
rgb[i] += rgb[i].length === 1 ? rgb[i] : "";
}
return (hash ? "#" : "") + rgb.join("");
}
console.log(applySat(50, "#ff0000")); // "#7f0000";
console.log(applySat(50, "ff0000")); // "7f0000";
console.log(applySat(50, "#fed")); // "#7f776f"
console.log(applySat(50, "fed")); // "7f776f"
console.log(applySat(20, "#addfaa")); // "#232d22"
If you really don't want to use a library, see mjijackson's RGB to HSL conversion page.
Copy the code to do RGB hex to HSL (or HSV) conversions. As the slider is moved, you'll need to use these to convert between the color models to get the saturation value, modify it, and then get the resulting rgb color back.
Note: HSL and HSV are standard color models. A few of the other answers are proposing definitions of "saturation" that do not correspond to these standard color models. Users will be confused as the alternate definitions will not give results consistent with what they'd expect from GIMP, Photoshop or other common graphics applications.
Real simple and easy function, input your color, and add how much you want to desaturate it by.
it does a pretty good job, but its not perfectly accurate.
function addSaturation(color, amount){
var color = color.replace('#', '').split('');
var letters = '0123456789ABCDEF'.split('');
for(var i = 0; i < color.length; i++){
var newSaturation = 0;
if(letters.indexOf(color[i]) + amount > 15) newSaturation = 15;
else if(letters.indexOf(color[i]) + amount < 0) newSaturation = 0;
else newSaturation = letters.indexOf(color[i]) + amount;
color[i] = letters[newSaturation];
}
return "#" + color.join('');
}
you can use positive or negative amounts as well.
You could utilize the Javascript provided in this solution to match your needs
To change the saturation of an element, shift each of the three HEX values simultaneously, bringing the values closer to 128 (half of 256).
background-color: #FF0000; // rgb(255, 0, 0)
to this...
background-color: #BF4040; // rgb(191, 64, 64)

Working with hex strings and hex values more easily in Javascript

I have some code which takes strings representing hexadecimal numbers - hex colors, actually - and adds them. For example, adding aaaaaa and 010101 gives the output ababab.
However, my method seems unnecessarily long and complicated:
var hexValue = "aaaaaa";
hexValue = "0x" + hexValue;
hexValue = parseInt(hexValue, 16);
hexValue = hexValue + 0x010101;
hexValue = hexValue.toString(16);
document.write(hexValue); // outputs 'ababab'
The hex value is still a string after concatenating 0x, so then I have to change it to a number, then I can add, and then I have to change it back into hex format! There are even more steps if the number I'm adding to it is a hexadecimal string to begin with, or if you take into consideration that I am removing the # from the hex color before all this starts.
Surely there's a simpler way to do such simple hexadecimal calculations! And just to be clear, I don't mean just putting it all on one line like (parseInt("0x"+"aaaaaa",16)+0x010101).toString(16) or using shorthand - I mean actually doing less operations.
Is there some way to get javascript to stop using decimal for all of its mathematical operations and use hex instead? Or is there some other method of making JS work with hex more easily?
No, there is no way to tell the JavaScript language to use hex integer format instead of decimal by default. Your code is about as concise as it gets but note that you do not need to prepend the "0x" base indicator when you use "parseInt" with a base.
Here is how I would approach your problem:
function addHexColor(c1, c2) {
var hexStr = (parseInt(c1, 16) + parseInt(c2, 16)).toString(16);
while (hexStr.length < 6) { hexStr = '0' + hexStr; } // Zero pad.
return hexStr;
}
addHexColor('aaaaaa', '010101'); // => 'ababab'
addHexColor('010101', '010101'); // => '020202'
As mentioned by a commenter, the above solution is chock full of problems, so below is a function that does proper input validation and adds color channels separately while checking for overflow.
function addHexColor2(c1, c2) {
const octetsRegex = /^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i
const m1 = c1.match(octetsRegex)
const m2 = c2.match(octetsRegex)
if (!m1 || !m2) {
throw new Error(`invalid hex color triplet(s): ${c1} / ${c2}`)
}
return [1, 2, 3].map(i => {
const sum = parseInt(m1[i], 16) + parseInt(m2[i], 16)
if (sum > 0xff) {
throw new Error(`octet ${i} overflow: ${m1[i]}+${m2[i]}=${sum.toString(16)}`)
}
return sum.toString(16).padStart(2, '0')
}).join('')
}
addHexColor2('aaaaaa', 'bogus!') // => Error: invalid hex color triplet(s): aaaaaa / bogus!
addHexColor2('aaaaaa', '606060') // => Error: octet 1 overflow: aa+60=10a
How about this:
var hexValue = "aaaaaa";
hexValue = (parseInt(hexValue, 16) + 0x010101).toString(16);
document.writeln(hexValue); // outputs 'ababab'
There is no need to add the 0x prefix if you use parseInt.
I think accepted answer is wrong. Hexadecimal color representation is not a linear. But instead, 3 sets of two characters are given to R, G & B.
So you can't just add a whole number and expect to RGB to add up correctly.
For Example
n1 = '005500'; <--- green
n2 = '00ff00'; <--- brighter green
Adding these numbers should result in a greener green.
In no way, adding greens should increase RED to increase. but by doing what accepted answer is doing, as in just treat whole number as one number then you'd carry over for numbers adding upto greater than f, f+1 = 10.
you get `015400` so by adding greens the RED increased .... WRONG
adding 005500 + 00ff00 should result in, = 00ff00. You can't add more green to max green.
For folks looking for a function that can add and subtract HEX colors without going out of bounds on an individual tuple, I wrote this function a few minutes ago to do just that:
export function shiftColor(base, change, direction) {
const colorRegEx = /^\#?[A-Fa-f0-9]{6}$/;
// Missing parameter(s)
if (!base || !change) {
return '000000';
}
// Invalid parameter(s)
if (!base.match(colorRegEx) || !change.match(colorRegEx)) {
return '000000';
}
// Remove any '#'s
base = base.replace(/\#/g, '');
change = change.replace(/\#/g, '');
// Build new color
let newColor = '';
for (let i = 0; i < 3; i++) {
const basePiece = parseInt(base.substring(i * 2, i * 2 + 2), 16);
const changePiece = parseInt(change.substring(i * 2, i * 2 + 2), 16);
let newPiece = '';
if (direction === 'add') {
newPiece = (basePiece + changePiece);
newPiece = newPiece > 255 ? 255 : newPiece;
}
if (direction === 'sub') {
newPiece = (basePiece - changePiece);
newPiece = newPiece < 0 ? 0 : newPiece;
}
newPiece = newPiece.toString(16);
newPiece = newPiece.length < 2 ? '0' + newPiece : newPiece;
newColor += newPiece;
}
return newColor;
}
You pass your base color as parameter 1, your change as parameter 2, and then 'add' or 'sub' as the last parameter depending on your intent.

FireFox and IE convert original color values to RGB

UPDATE: the problem is with FF's .cloneNode() method: http://jsfiddle.net/beCVL/1/
I know FF and IE internally convert color to RGB, which causes problem, because the color values don't match what is on the server.
Proof:
Chrome 18:
CKEDITOR.instances.selected_text_actual.getData()
>> "s <span style="color: #ff0000">text</span>"
FireFox 11:
CKEDITOR.instances.selected_text_actual.getData()
>> "s <span style="color: rgb(255, 0, 0);">text</span>"
So, the way I want to solve the problem is to make CKEditor's data processor always use the rgb values. Is there a way to do that?
I found that something like this should work:
CKEDITOR.on( 'instanceReady', function( ev ){
var editor = ev.editor,
dataProcessor = editor.dataProcessor,
htmlFilter = dataProcessor && dataProcessor.htmlFilter;
// HTML 4 way to end tags
dataProcessor.writer.selfClosingEnd = '>';
htmlFilter.addRules({
elements:{
$:function(element){
var e = jQuery(element);
e.css("color", e.css("color")); // jquery auto converts to rgb
}
}
});
});
Source: http://sebduggan.com/blog/customising-ckeditor-settings-in-mura/
but, there is no change.
The conversion to RGB is pretty straight forward:
a.attr("style", "color: #444")
[
<div style=​"color:​ #444">​</div>​
]
a.css("color", a.css("color"));
[
<div style=​"color:​ rgb(68, 68, 68)​;​ ">​</div>​
]
EDIT: the problem is with FF's .cloneNode() method: http://jsfiddle.net/beCVL/1/
As I replied to you in http://cksource.com/forums/viewtopic.php?f=11&t=25141 you can use the original "output HTML" sample that contains the full code that has been copied in the blog that you linked and use the convertRGBToHex function as it does.
And BTW, Firefox respects the styles, the only browser that currently changes that part is IE.
If you want to compare two color values that can each be represented in several different ways, then you have to make sure both are converted to a canonical form (e.g. the exact same form).
So, you can use rgb(x,y,z) as your canonical form if you want, but you will have to make sure that any color values expressed as #xyz or #xxyyzz are first converted to the rgb form before comparing.
Here's a function that converts three values of colors all to rgb(x,y,z) with no spaces and then compares them and returns to you the result:
function colorsAreSame(c1, c2) {
var space = /\s+/g;
function makeRGB(c) {
var r, g, b;
c = c.replace(space, "");
if (c.charAt(0) == "#") {
if (c.length == 4) {
r = parseInt(c.charAt(1), 16);
r = (r * 16) + r;
g = parseInt(c.charAt(2), 16);
g = (g * 16) + g;
b = parseInt(c.charAt(3), 16);
b = (b * 16) + b;
} else if (c.length == 7) {
r = parseInt(c.substr(1, 2), 16);
g = parseInt(c.substr(3, 2), 16);
b = parseInt(c.substr(5, 2), 16);
}
return "rgb(" + r + "," + g + "," + b + ")";
} else {
return(c);
}
}
c1 = makeRGB(c1);
c2 = makeRGB(c2);
return(c1 == c2);
}
The fact that cloning a node changes the style attribute is almost certainly a bug in Gecko but in the mean time element.style.color will return rgb(255, 0, 0) in both cases.

Categories

Resources