Create a hexadecimal colour based on a string with JavaScript - javascript

I want to create a function that will accept any old string (will usually be a single word) and from that somehow generate a hexadecimal value between #000000 and #FFFFFF, so I can use it as a colour for a HTML element.
Maybe even a shorthand hex value (e.g: #FFF) if that's less complicated. In fact, a colour from a 'web-safe' palette would be ideal.

Here's an adaptation of CD Sanchez' answer that consistently returns a 6-digit colour code:
var stringToColour = function(str) {
var hash = 0;
for (var i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
}
var colour = '#';
for (var i = 0; i < 3; i++) {
var value = (hash >> (i * 8)) & 0xFF;
colour += ('00' + value.toString(16)).substr(-2);
}
return colour;
}
Usage:
stringToColour("greenish");
// -> #9bc63b
Example:
http://jsfiddle.net/sUK45/
(An alternative/simpler solution might involve returning an 'rgb(...)'-style colour code.)

Just porting over the Java from Compute hex color code for an arbitrary string to Javascript:
function hashCode(str) { // java String#hashCode
var hash = 0;
for (var i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
}
return hash;
}
function intToRGB(i){
var c = (i & 0x00FFFFFF)
.toString(16)
.toUpperCase();
return "00000".substring(0, 6 - c.length) + c;
}
To convert you would do:
intToRGB(hashCode(your_string))

I wanted similar richness in colors for HTML elements, I was surprised to find that CSS now supports hsl() colors, so a full solution for me is below:
Also see How to automatically generate N "distinct" colors? for more alternatives more similar to this.
Edit: updating based on #zei's version (with american spelling)
var stringToColor = (string, saturation = 100, lightness = 75) => {
let hash = 0;
for (let i = 0; i < string.length; i++) {
hash = string.charCodeAt(i) + ((hash << 5) - hash);
hash = hash & hash;
}
return `hsl(${(hash % 360)}, ${saturation}%, ${lightness}%)`;
}
// For the sample on stackoverflow
function colorByHashCode(value) {
return "<span style='color:" + stringToColor(value) + "'>" + value + "</span>";
}
document.body.innerHTML = [
"javascript",
"is",
"nice",
].map(colorByHashCode).join("<br/>");
span {
font-size: 50px;
font-weight: 800;
}
In HSL its Hue, Saturation, Lightness. So the hue between 0-359 will get all colors, saturation is how rich you want the color, 100% works for me. And Lightness determines the deepness, 50% is normal, 25% is dark colors, 75% is pastel. I have 30% because it fit with my color scheme best.

Here is my 2021 version with Reduce Function and HSL Color.
function getBackgroundColor(stringInput) {
let stringUniqueHash = [...stringInput].reduce((acc, char) => {
return char.charCodeAt(0) + ((acc << 5) - acc);
}, 0);
return `hsl(${stringUniqueHash % 360}, 95%, 35%)`;
}

Using the hashCode as in Cristian Sanchez's answer with hsl and modern javascript, you can create a color picker with good contrast like this:
function hashCode(str) {
let hash = 0;
for (var i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
}
return hash;
}
function pickColor(str) {
return `hsl(${hashCode(str) % 360}, 100%, 80%)`;
}
one.style.backgroundColor = pickColor(one.innerText)
two.style.backgroundColor = pickColor(two.innerText)
div {
padding: 10px;
}
<div id="one">One</div>
<div id="two">Two</div>
Since it's hsl, you can scale luminance to get the contrast you're looking for.
function hashCode(str) {
let hash = 0;
for (var i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
}
return hash;
}
function pickColor(str) {
// Note the last value here is now 50% instead of 80%
return `hsl(${hashCode(str) % 360}, 100%, 50%)`;
}
one.style.backgroundColor = pickColor(one.innerText)
two.style.backgroundColor = pickColor(two.innerText)
div {
color: white;
padding: 10px;
}
<div id="one">One</div>
<div id="two">Two</div>

I find that generating random colors tends to create colors that do not have enough contrast for my taste. The easiest way I have found to get around that is to pre-populate a list of very different colors. For every new string, assign the next color in the list:
// Takes any string and converts it into a #RRGGBB color.
var StringToColor = (function(){
var instance = null;
return {
next: function stringToColor(str) {
if(instance === null) {
instance = {};
instance.stringToColorHash = {};
instance.nextVeryDifferntColorIdx = 0;
instance.veryDifferentColors = ["#000000","#00FF00","#0000FF","#FF0000","#01FFFE","#FFA6FE","#FFDB66","#006401","#010067","#95003A","#007DB5","#FF00F6","#FFEEE8","#774D00","#90FB92","#0076FF","#D5FF00","#FF937E","#6A826C","#FF029D","#FE8900","#7A4782","#7E2DD2","#85A900","#FF0056","#A42400","#00AE7E","#683D3B","#BDC6FF","#263400","#BDD393","#00B917","#9E008E","#001544","#C28C9F","#FF74A3","#01D0FF","#004754","#E56FFE","#788231","#0E4CA1","#91D0CB","#BE9970","#968AE8","#BB8800","#43002C","#DEFF74","#00FFC6","#FFE502","#620E00","#008F9C","#98FF52","#7544B1","#B500FF","#00FF78","#FF6E41","#005F39","#6B6882","#5FAD4E","#A75740","#A5FFD2","#FFB167","#009BFF","#E85EBE"];
}
if(!instance.stringToColorHash[str])
instance.stringToColorHash[str] = instance.veryDifferentColors[instance.nextVeryDifferntColorIdx++];
return instance.stringToColorHash[str];
}
}
})();
// Get a new color for each string
StringToColor.next("get first color");
StringToColor.next("get second color");
// Will return the same color as the first time
StringToColor.next("get first color");
While this has a limit to only 64 colors, I find most humans can't really tell the difference after that anyway. I suppose you could always add more colors.
While this code uses hard-coded colors, you are at least guaranteed to know during development exactly how much contrast you will see between colors in production.
Color list has been lifted from this SO answer, there are other lists with more colors.

If your inputs are not different enough for a simple hash to use the entire color spectrum, you can use a seeded random number generator instead of a hash function.
I'm using the color coder from Joe Freeman's answer, and David Bau's seeded random number generator.
function stringToColour(str) {
Math.seedrandom(str);
var rand = Math.random() * Math.pow(255,3);
Math.seedrandom(); // don't leave a non-random seed in the generator
for (var i = 0, colour = "#"; i < 3; colour += ("00" + ((rand >> i++ * 8) & 0xFF).toString(16)).slice(-2));
return colour;
}

I have opened a pull request to Please.js that allows generating a color from a hash.
You can map the string to a color like so:
const color = Please.make_color({
from_hash: "any string goes here"
});
For example, "any string goes here" will return as "#47291b"
and "another!" returns as "#1f0c3d"

Javascript Solution inspired by Aslam's solution but returns a color in hex color code
/**
*
* #param {String} - stringInput - 'xyz'
* #returns {String} - color in hex color code - '#ae6204'
*/
function getBackgroundColor(stringInput) {
const h = [...stringInput].reduce((acc, char) => {
return char.charCodeAt(0) + ((acc << 5) - acc);
}, 0);
const s = 95, l = 35 / 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)}`;
}

Yet another solution for random colors:
function colorize(str) {
for (var i = 0, hash = 0; i < str.length; hash = str.charCodeAt(i++) + ((hash << 5) - hash));
color = Math.floor(Math.abs((Math.sin(hash) * 10000) % 1 * 16777216)).toString(16);
return '#' + Array(6 - color.length + 1).join('0') + color;
}
It's a mixed of things that does the job for me.
I used JFreeman Hash function (also an answer in this thread) and Asykäri pseudo random function from here and some padding and math from myself.
I doubt the function produces evenly distributed colors, though it looks nice and does that what it should do.

Here's a solution I came up with to generate aesthetically pleasing pastel colours based on an input string. It uses the first two chars of the string as a random seed, then generates R/G/B based on that seed.
It could be easily extended so that the seed is the XOR of all chars in the string, rather than just the first two.
Inspired by David Crow's answer here: Algorithm to randomly generate an aesthetically-pleasing color palette
//magic to convert strings to a nice pastel colour based on first two chars
//
// every string with the same first two chars will generate the same pastel colour
function pastel_colour(input_str) {
//TODO: adjust base colour values below based on theme
var baseRed = 128;
var baseGreen = 128;
var baseBlue = 128;
//lazy seeded random hack to get values from 0 - 256
//for seed just take bitwise XOR of first two chars
var seed = input_str.charCodeAt(0) ^ input_str.charCodeAt(1);
var rand_1 = Math.abs((Math.sin(seed++) * 10000)) % 256;
var rand_2 = Math.abs((Math.sin(seed++) * 10000)) % 256;
var rand_3 = Math.abs((Math.sin(seed++) * 10000)) % 256;
//build colour
var red = Math.round((rand_1 + baseRed) / 2);
var green = Math.round((rand_2 + baseGreen) / 2);
var blue = Math.round((rand_3 + baseBlue) / 2);
return { red: red, green: green, blue: blue };
}
GIST is here: https://gist.github.com/ro-sharp/49fd46a071a267d9e5dd

Here is another try:
function stringToColor(str){
var hash = 0;
for(var i=0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 3) - hash);
}
var color = Math.abs(hash).toString(16).substring(0, 6);
return "#" + '000000'.substring(0, 6 - color.length) + color;
}

All you really need is a good hash function. On node, I just use
const crypto = require('crypto');
function strToColor(str) {
return '#' + crypto.createHash('md5').update(str).digest('hex').substr(0, 6);
}

After having a look at the rather code intensive and rather old answers, I thought I'd review this issue from a 2021 standpoint just for fun, hope it is of use to anyone. Having the HSL color model and the crypto API implemented in pretty much all browsers (except IE of course) today, it could be solved as simple as that:
async function getColor(text, minLightness = 40, maxLightness = 80, minSaturation = 30, maxSaturation = 100) {
let hash = await window.crypto.subtle.digest("SHA-1", new TextEncoder().encode(text));
hash = new Uint8Array(hash).join("").slice(16);
return "hsl(" + (hash % 360) + ", " + (hash % (maxSaturation - minSaturation) + minSaturation) + "%, " + (hash % (maxLightness - minLightness) + minLightness) + "%)";
}
function generateColor() {
getColor(document.getElementById("text-input").value).then(color => document.querySelector(".swatch").style.backgroundColor = color);
}
input {
padding: 5px;
}
.swatch {
margin-left: 10px;
width: 28px;
height: 28px;
background-color: white;
border: 1px solid gray;
}
.flex {
display: flex;
}
<html>
<body>
<div class="flex">
<form>
<input id="text-input" type="text" onInput="generateColor()" placeholder="Type here"></input>
</form>
<div class="swatch"></div>
</div>
</body>
</html>
This should be way faster than generating hashes manually, and also offers a way to define saturation and lightness in case you don't want colors that are too flat or too bright or too dark (e.g. if you want to write text on those colors).

2023 version plain and simple TypeScript arrow function that returns HSL color.
const stringToColor = (value: string) => {
let hash = 0;
for (let i = 0; i < value.length; i++) {
hash = value.charCodeAt(i) + ((hash << 5) - hash);
}
return `hsl(${hash % 360}, 85%, 35%)`;
};

This function does the trick. It's an adaptation of this, fairly longer implementation this repo ..
const color = (str) => {
let rgb = [];
// Changing non-hexadecimal characters to 0
str = [...str].map(c => (/[0-9A-Fa-f]/g.test(c)) ? c : 0).join('');
// Padding string with zeroes until it adds up to 3
while (str.length % 3) str += '0';
// Dividing string into 3 equally large arrays
for (i = 0; i < str.length; i += str.length / 3)
rgb.push(str.slice(i, i + str.length / 3));
// Formatting a hex color from the first two letters of each portion
return `#${rgb.map(string => string.slice(0, 2)).join('')}`;
}

I have a situation where i want to display a background based on the username of the user and display the username's first letter on top.
i used the belove code for that it worked well for me
var stringToColour = function (str) {
var hash = 0;
for (var i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
}
var colour = '#';
for (var i = 0; i < 3; i++) {
var value = (hash >> (i * 8)) & 0xff;
colour += ('00' + value.toString(16)).substr(-2);
}
return colour;}
To find the appropriate colour you can use
function lightOrDark(color) {
// Check the format of the color, HEX or RGB?
if (color.match(/^rgb/)) {
// If HEX --> store the red, green, blue values in separate variables
color = color.match(
/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/,
);
var r = color[1];
var g = color[2];
var b = color[3];
} else {
// If RGB --> Convert it to HEX: http://gist.github.com/983661
color = +(
'0x' + color.slice(1).replace(color.length < 5 && /./g, '$&$&')
);
r = color >> 16;
g = (color >> 8) & 255;
b = color & 255;
}
// HSP equation from http://alienryderflex.com/hsp.html
var hsp = Math.sqrt(0.299 * (r * r) + 0.587 * (g * g) + 0.114 * (b * b));
// Using the HSP value, determine whether the color is light or dark
if (hsp > 127.5) {
return 'light';
} else {
return 'dark';
}
}

my code is for Java.
Thanks for all.
public static int getColorFromText(String text)
{
if(text == null || text.length() < 1)
return Color.BLACK;
int hash = 0;
for (int i = 0; i < text.length(); i++)
{
hash = text.charAt(i) + ((hash << 5) - hash);
}
int c = (hash & 0x00FFFFFF);
c = c - 16777216;
return c;
}

I convert this in one line for Python
import hashlib
hash = hashlib.sha1(b'user#email.com').hexdigest()
print("#" + hash[0:6])

Related

Color Spectrum Optimization

I have the following implementation, it works and functional. I am checking if fname properties are same in the following javascript object, then I assign the same color for these paired objects.
Here is one javascript object sample:
{"value": 10,"series": 1,"category": "LG","fname": "","valueColor": ""},
However, I would like to use more distinguished colors, rather than very similar color, for example in the given fiddle, colors are almost all in green spectrum. Also I do not want to give any color value where value property equals to 0
Here is the core implementation
function colorSpectrum(N) {
var colorMap = [], inc = 50, start = 1000;
for (i = start; i < start+N*inc; i+=inc) {
var num = ((4095 * i) >>> 0).toString(16);
while (num.length < 3) {
num = "0" + num;
}
colorMap.push("#" + num);
}
return colorMap;
}
function process(data){
var map = {}, colorMap = colorSpectrum(data.length);
data.forEach(function(item, index){
if(!map.hasOwnProperty(item.fname)){
map[item.fname] = colorMap[index];
}
data[index].valueColor = map[item.fname];
});
return data;
}
FIDDLE
Try picking random colors
function colorSpectrum(N) {
var colorMap = [];
for (i = 0; i < N; i+=1) {
var color = getRndColor()
colorMap.push("#"+color);
}
return colorMap;
}
function getRndColor() {
var n = Math.floor(Math.random()*255*255*255);
var hex = Number(n).toString(16);
while(hex.length < 6) {
hex = "0"+hex;
}
return hex;
}
If you want a full range of colors from black to white, you need to change this part:
var colorMap = [], inc = 50, start = 1000;
for (i = start; i < start+N*inc; i+=inc) {
You see, the loop starts from 1000, which is the color #3e8, already green. The scale should go from 0 to 4095 (for 3-character values like #007, #abc, etc...), with having the increment based on the amount of data.
However, I'd suggest getting at least a little bit of the control by having all RGB components generated separately instead of the full HEX value right away.

Sort Colour / Color Values

Im looking to align the following array of colours as precisely as possible.
After searching & trying many solutions suggested on Stackoverflow, the pusher.color library has the best solution, however, it too is far from perfect. I would like to hear solutions on how we can align them perfectly.
JSFIDDLE LINK : http://jsfiddle.net/dxux7y3e/
Code:
var coloursArray=['#FFE9E9','#B85958','#FFB1AE','#FFC2BF','#C55E58','#FFC7C4','#FF9A94','#FF9D96','#FA9790','#A78B88','#A78B88','#CE675B','#DB8073','#FF9D90','#FF7361','#FFD6D1','#F9A092','#FF7B67','#EBACA2','#FF806D','#DD6D5B','#D16654','#ED8673','#FFC4B8','#E2725B','#ED7A64','#8F3926','#BD492F','#9D3C27','#AD533E','#BF4024','#FFC9BC','#6B6766','#E1CDC8','#C2654C','#B3978F','#FFC7B8','#CE2B00','#C2654C','#A24D34','#FF926D','#E78667','#FFB198','#8C756D','#9E6D5B','#FFC7B0','#FFBEA4','#D2B9AF','#FFB193','#632710','#B26746','#976854','#F44900','#E79873','#EFA27F','#532510','#BC866B','#FDE5D9','#FF5B00','#D18C67','#FF5B00','#9E4312','#763713','#BB6B39','#B5622E','#CC7742','#6D4227','#B56B38','#FF7518','#F3B080','#995C30','#995C30','#FF6A00','#D89769','#71472A','#EDAC7B','#EEAB79','#EBCFB9','#FBE3D1','#E19255','#5E381B','#FFDCC1','#FFF0E4','#F68D39','#7B5B40','#FF8313','#FFCEA4','#AA8667','#975414','#CB9867','#8C5B2B','#FFCE9E','#7B4714','#FFF3E7','#FFA449','#CEAF90','#CDB69E','#EFD6BC','#DDA66B','#B27737','#B88A57','#CE9B61','#F4C38B','#543817','#BC9C78','#DBB07A','#FF8E04','#F6EADB','#DBC2A4','#C49B64','#CBA26B','#80551E','#FF9200','#FFECD3','#FFC87C','#FFB755','#DBB680','#D2D0CD','#EFDBBE','#E5C18B','#FFE5BC','#F2EADB','#885F12','#FFE7B6','#825A08','#906712','#F2D18E','#C8C6C2','#FFB000','#FFC243','#C6BEAD','#D0C3A4','#916800','#8C6700','#F4E9CA','#FFF0C5','#FFE080','#FFEBA8','#846600','#FFE692','#F5F0DB','#433F2F','#BBB394','#FFEFAA','#FFE76D','#FFFAE0','#3E3B28','#554900','#E1E0D8','#74725C','#605F54','#F8F7DD','#A5A467','#DDDDDA','#FFFFEE','#A3A39D','#E0E0D7','#BEBEB9','#E8E8E5','#454531','#ACACAA','#E9E9DF','#FFFFDC','#EBEBE7','#979831','#C5C6BE','#B9C866','#898D72','#F3FAD1','#616452','#CED5B0','#A1A787','#595C4E','#B0BB8C','#EEFFB6','#ACB78E','#8FA359','#858F6C','#86916E','#374912','#AEB0AA','#79904C','#627739','#747F60','#9FA98E','#E7F9CB','#E1F9BE','#495637','#8A9978','#4E5F39','#86996E','#C3CEB7','#78866B','#CEDDC1','#B5CEA2','#536149','#D6E6CC','#D6E6CC','#809873','#4F564C','#4F6C45','#555F52','#4F7942','#5F705B','#D0DFCD','#2B3929','#F0F7EF','#AAD5A4','#99BC95','#B6D4B4','#869E86','#618661','#006700','#E9EEE9','#739E73','#005B06','#EDF7EE','#D0E0D2','#809784','#ABCEB1','#C0E0C8','#3A5241','#435549','#E6ECE8','#E3EAE6','#3B604C','#00602F','#92B7A5','#2F5B49','#318061','#30745B','#316955','#00A275','#C2D1CE','#80A7A0','#00A082','#C2D1CF','#5C6E6C','#607473','#EDF7F7','#1E8285','#D5E7E8','#AADEE1','#188086','#107F87','#566364','#007B86','#66949A','#CAE2E5','#18656F','#004F61','#0C5B6C','#668E98','#BBD0DA','#91B4C5','#AFC3CD','#738A99','#3A5467','#476174','#244967','#556C80','#667A8C','#516D87','#1E4263','#7C8791','#849CB6','#738CAA','#1E3A5F','#1E3655','#9EB0CE','#B6BAC2','#67738D','#BEC1CD','#555559','#616180','#000049','#000031','#F8F8FC','#938BA4','#47375D','#F7F6F8','#3D0067','#514C53','#9566A2','#7F5482','#A279A4','#6D1261','#A06492','#925582','#945B80','#CE94BA','#ECCFE1','#A20058','#A6005B','#BC0061','#BB0061','#F3CEE1','#B3005B','#AB165F','#8A184D','#AA185B','#F3DAE4','#DB3779','#E71261','#E74F86','#FFD6E5','#BE9BA7','#D0396A','#DB1855','#F798B6','#9C294A','#D62B5B','#DE3969','#BC1641','#E7547A','#D52756','#9C7D85','#DB244F','#A1354F','#C22443','#FFBDCA','#8B6D73','#DC3D5B','#FF738C','#F13154','#BC4055','#FED4DB','#FFCFD6','#CB4E61','#ED455A','#F36C7B','#C94F5B','#F3959D','#A8444C','#FFCCD0','#735B5D','#D15D67','#B44B52','#FD868D','#FFD5D8','#C3767B','#FF8087','#C8242B','#FFEAEB','#F95A61','#E96D73','#E6656B','#FF6D73','#FF555B','#A35A5B','#FFD3D4','#B84B4D'];
var body=document.getElementsByTagName('body')[0];
function hexToRgb(hex) {
hex = hex.substring(1, hex.length);
var r = parseInt((hex).substring(0, 2), 16);
var g = parseInt((hex).substring(2, 4), 16);
var b = parseInt((hex).substring(4, 6), 16);
return r + "," + g + "," + b;
}
function rgbToHex(r, g, b) {
return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
}
var rgbArr=new Array();
var div=document.createElement('div');
div.id='Original';
body.appendChild(div);
for(var color in coloursArray){
color=coloursArray[color];
displayColor(color,div);
rgbArr.push(hexToRgb(color));
}
var hslArr=new Array();
for(var i=0;i<rgbArr.length;i++){
//Transforming rgb to hsl
//`hslArr[i][1]` (`i`) is a reference to the rgb color, in order to retrieve it later
hslArr[i]=[rgbToHsl(rgbArr[i]),i];
}
var sortedHslArr=new Array();
//Sorting `hslArr` into `sortedHslArr`
outerloop:
for(var i=0;i<hslArr.length;i++){
for(var j=0;j<sortedHslArr.length;j++){
if(sortedHslArr[j][0][0]>hslArr[i][0][0]){
sortedHslArr.splice(j,0,hslArr[i]);
continue outerloop;
}
}
sortedHslArr.push(hslArr[i]);
}
var sortedRgbArr=new Array();
//Retrieving rgb colors
for(var i=0;i<sortedHslArr.length;i++){
sortedRgbArr[i]=rgbArr[sortedHslArr[i][1]];
}
function displayColor(color,parent){
var div;
div=document.createElement('div');
div.style.backgroundColor=color;
div.style.width='22px';
div.style.height='22px';
div.style.cssFloat='left';
div.style.position='relative';
parent.appendChild(div);
}
var finalArray=new Array();
var div=document.createElement('div');
div.id='Sorted';
body.appendChild(div);
for(var color in sortedRgbArr){
color=sortedRgbArr[color];
color=color.split(',');
color=rgbToHex(parseInt(color[0]),parseInt(color[1]),parseInt(color[2]));
displayColor(color,div);
finalArray.push(color);
}
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 sorted = coloursArray.sort(function(colorA, colorB) {
return pusher.color(colorA).hue() - pusher.color(colorB).hue();
});
// console.log(sorted);
var div=document.createElement('div');
div.id='Pusher';
body.appendChild(div);
for(var color in sorted){
color=sorted[color];
displayColor(color,div);
}
var div=document.createElement('div');
body.appendChild(div);
var str='';
for(var color in sorted){
color=sorted[color];
str+='\''+color+'\',';
}
div.innerHTML=str;
function sorthueColors (colors) {
for (var c = 0; c < colors.length; c++) {
/* Get the hex value without hash symbol. */
var hex = colors[c].substring(1);
//var hex = colors[c].hex.substring(1);
/* Get the RGB values to calculate the Hue. */
var r = parseInt(hex.substring(0,2),16)/255;
var g = parseInt(hex.substring(2,4),16)/255;
var b = parseInt(hex.substring(4,6),16)/255;
/* Getting the Max and Min values for Chroma. */
var max = Math.max.apply(Math, [r,g,b]);
var min = Math.min.apply(Math, [r,g,b]);
/* Variables for HSV value of hex color. */
var chr = max-min;
var hue = 0;
var val = max;
var sat = 0;
if (val > 0) {
/* Calculate Saturation only if Value isn't 0. */
sat = chr/val;
if (sat > 0) {
if (r == max) {
hue = 60*(((g-min)-(b-min))/chr);
if (hue < 0) {hue += 360;}
} else if (g == max) {
hue = 120+60*(((b-min)-(r-min))/chr);
} else if (b == max) {
hue = 240+60*(((r-min)-(g-min))/chr);
}
}
}
/* Modifies existing objects by adding HSV values. */
colors[c].hue = hue;
colors[c].sat = sat;
colors[c].val = val;
}
/* Sort by Hue. */
return colors.sort(function(a,b){return a.hue - b.hue;});
}
Problem is, sorting requires a well-defined order - in other words, you need to map all colors to one dimension. While there are some approaches to display the color space in two dimensions, I am not aware of any that would display it in one dimension and still make sense to the human eye.
However, if you don't insist on some universal ordering and you merely want to place a given list of colors in a way that looks nice, then some clustering approach might produce better results. I tried the naive approach, the idea here is to put similar colors into the same cluster and to merge these clusters until you only have one. Here is the code I've got:
function colorDistance(color1, color2) {
// This is actually the square of the distance but
// this doesn't matter for sorting.
var result = 0;
for (var i = 0; i < color1.length; i++)
result += (color1[i] - color2[i]) * (color1[i] - color2[i]);
return result;
}
function sortColors(colors) {
// Calculate distance between each color
var distances = [];
for (var i = 0; i < colors.length; i++) {
distances[i] = [];
for (var j = 0; j < i; j++)
distances.push([colors[i], colors[j], colorDistance(colors[i], colors[j])]);
}
distances.sort(function(a, b) {
return a[2] - b[2];
});
// Put each color into separate cluster initially
var colorToCluster = {};
for (var i = 0; i < colors.length; i++)
colorToCluster[colors[i]] = [colors[i]];
// Merge clusters, starting with lowest distances
var lastCluster;
for (var i = 0; i < distances.length; i++) {
var color1 = distances[i][0];
var color2 = distances[i][1];
var cluster1 = colorToCluster[color1];
var cluster2 = colorToCluster[color2];
if (!cluster1 || !cluster2 || cluster1 == cluster2)
continue;
// Make sure color1 is at the end of its cluster and
// color2 at the beginning.
if (color1 != cluster1[cluster1.length - 1])
cluster1.reverse();
if (color2 != cluster2[0])
cluster2.reverse();
// Merge cluster2 into cluster1
cluster1.push.apply(cluster1, cluster2);
delete colorToCluster[color1];
delete colorToCluster[color2];
colorToCluster[cluster1[0]] = cluster1;
colorToCluster[cluster1[cluster1.length - 1]] = cluster1;
lastCluster = cluster1;
}
// By now all colors should be in one cluster
return lastCluster;
}
Complete fiddle
The colorDistance() function works with RGB colors that are expressed as arrays with three elements. There are certainly much better approaches to do this and they might produce results that look better. Note also that this isn't the most efficient algorithm because it calculates the distance between each and every color (O(n2)) so if you have lots of colors you might want to optimize it.
Assuming there is no best solution. Here is how i would find what i like the most.
First i would use color distance function:
var balance = [10, 0, 0.01];
function colorDistance(color1, color2) {
var result = 0;
color1 = rgbToHsl(color1[0], color1[1], color1[2]);
color2 = rgbToHsl(color2[0], color2[1], color2[2]);
for (var i = 0; i < color1.length; i++)
result += (color1[i] - color2[i]) * (color1[i] - color2[i]) * balance[i];
return result;
}
I would use balance function :
hue distance var balance = [1, 0, 0];
saturation distance var balance = [0, 1, 0];
lightness distance var balance = [0, 0, 1];
After that you can just experiment and pick what you like. Note that values are not normalized to appear between 0 to 1 but other then that it should work well. You can also you another distance function this is just most trivial and well known one that gets decent results.
This approach does not give you the best solution, but describes a way to fiddle with it and get result you where looking for. Have fun.
Using the Hue of a color is usually a pleasant way to map the color on a one-dimensional scale. You could convert the rgb color to hsv using the answers here: RGB to HSV color in javascript?
This is an improved code based on pusher.color lib.
https://jsfiddle.net/kt1zv6g4/3/

ACE Text Editor displays text characters in place of spaces

I've written the following Javascript:
(function () {
var computationModule = (function foo1(stdlib, foreign, heap) {
"use asm";
var sqrt = stdlib.Math.sqrt,
heapArray = new stdlib.Int32Array(heap),
outR = 0.0,
outI = 0.0;
function computeRow(canvasWidth, canvasHeight, limit, max, rowNumber, minR, maxR, minI, maxI) {
canvasWidth = +canvasWidth;
canvasHeight = +canvasHeight;
limit = +limit;
max = max | 0;
rowNumber = +rowNumber;
minR = +minR;
maxR = +maxR;
minI = +minI;
maxI = +maxI;
var columnNumber = 0.0,
zReal = 0.0,
zImaginary = 0.0,
numberToEscape = 0;
var columnNumberInt = 0;
// Compute the imaginary part of the numbers that correspond to pixels in this row.
zImaginary = +(((maxI - minI) * +rowNumber) / +canvasHeight + minI);
// Iterate over the pixels in this row.
// Compute the number of iterations to escape for each pixel that will determine its color.
for (columnNumber = +0; + columnNumber < +canvasWidth; columnNumber = +(+columnNumber + 1.0)) {
// Compute the real part of the number for this pixel.
zReal = +(((maxR - minR) * +columnNumber) / +canvasWidth + minR);
numberToEscape = howManyToEscape(zReal, zImaginary, max, limit) | 0;
columnNumberInt = columnNumberInt + 1 | 0;
heapArray[(columnNumberInt * 4) >> 2] = numberToEscape | 0;
}
}
// Function to determine how many iterations for a point to escape.
function howManyToEscape(r, i, max, limit) {
r = +r;
i = +i;
max = max | 0;
limit = +limit;
var j = 0,
ar = 0.0,
ai = 0.0;
ar = +r;
ai = +i;
for (j = 0;
(j | 0) < (max | 0); j = (j + 1) | 0) {
iteratingFunction(ar, ai, r, i)
ar = outR;
ai = outI;
if (+(ar * ar + ai * ai) >= +(limit * limit))
return j | 0;
}
return j | 0;
}
// The iterating function defining the fractal to draw
// r and i are the real and imaginary parts of the value from the previous iteration
// r0 and i0 are the starting points
function iteratingFunction(r, i, r0, i0) {
r = +r;
i = +i;
r0 = +r0;
i0 = +i0;
computePower(r, i, 2);
// Set the output from this function to t
outR = +(r0 + outR);
outI = +(i0 + outI);
}
// Compute the result of [r,i] raised to the power n.
// Place the resulting real part in outR and the imaginary part in outI.
function computePower(r, i, n) {
// Tell asm.js that r, i are floating point and n is an integer.
r = +r;
i = +i;
n = n | 0;
// Declare and initialize variables to be numbers.
var rResult = 0.0;
var iResult = 0.0;
var j = 0;
var tr = 0.0;
var ti = 0.0;
// Declare and initialize variables that will be used only in the
// event we need to compute the reciprocal.
var abs = 0.0;
var recr = 0.0;
var reci = 0.0;
if ((n | 0) < (0 | 0)) {
// For n less than 0, compute the reciprocal and then raise it to the opposite power.
abs = +sqrt(r * r + i * i);
recr = r / abs;
reci = -i / abs;
r = recr;
i = reci;
n = -n | 0;
}
rResult = r;
iResult = i;
for (j = 1;
(j | 0) < (n | 0); j = (j + 1) | 0) {
tr = rResult * r - iResult * i;
ti = rResult * i + iResult * r;
rResult = tr;
iResult = ti;
}
outR = rResult;
outI = iResult;
} // end computePower
return {
computeRow: computeRow
};
})(self, foreign, heap);
// Return computationModule that we just defined.
return computationModule;
})();
There's nothing particularly unusual about this Javascript, except that I want to make my web application display the Javascript in an ACE text editor (http://ace.c9.io/) so that the user can modify the code at runtime.
I load the Javascript using jQuery AJAX and then set the contents of the ACE Editor to the Javascript code. After the user modifies the code, he can click a button to run the code. (This uses eval)
The problem I'm having is that ACE is displaying strange characters instead of spaces.
Oddly enough, if I try to copy text from the ACE editor, the strange characters disappear and the spaces are normal. Furthermore, the code runs fine even with these strange characters being displayed.
I also noticed that the problem does not appear when using Firefox, but it appears for Chrome and IE 11.
Finally, the problem only occurs when I put the code on a real web server. It doesn't reproduce in my development environment.
Looking at this some more, I can see that it's not just the text I'm loading via AJAX. Even when I type new spaces, more text characters appear!
What could be going wrong so that the text doesn't display properly?
Here's a link to the application: http://danielsadventure.info/html5fractal/
Use charset="utf-8" in the script tag where you include ace.
<script src="path/to/ace.js" charset="utf-8">
This may have something to do with this:
When no explicit charset parameter is provided by the sender, media
subtypes of the "text" type are defined to have a default charset
value of "ISO-8859-1" when received via HTTP. Data in character sets
other than "ISO-8859-1" or its subsets MUST be labeled with an
appropriate charset value. See section 3.4.1 for compatibility
problems.
http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7.1
So the string passed to the script are in an encoding different than what ACE (or JS in general) expects, which is UTF-8.

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)

Javascript color gradient

Using javascript with or without Jquery, I need to a create a gradient of colours based on a start and finish color. Is this possible to do programmatically?
The end colour is only ever going to be darker shade of the start colour and it's for an unordered list which I have no control over the number of li items. I'm looking for a solution that allows me to pick a start and end color, convert the hex value into RGB so it can be manipulated in code. The starting RGB values gets incremented by a step value calculated based upon the number of items.
so if the list had 8 items then the it needs to increment the seperate Red Green Blue values in 8 steps to achieve the final colour. Is there a better way to do it and if so where can I find some sample code?
I created a JS library, RainbowVis-JS to solve this general problem. You just have to set the number of items using setNumberRange and set the start and end colour using setSpectrum. Then you get the hex colour code with colourAt.
var numberOfItems = 8;
var rainbow = new Rainbow();
rainbow.setNumberRange(1, numberOfItems);
rainbow.setSpectrum('red', 'black');
var s = '';
for (var i = 1; i <= numberOfItems; i++) {
var hexColour = rainbow.colourAt(i);
s += '#' + hexColour + ', ';
}
document.write(s);
// gives:
// #ff0000, #db0000, #b60000, #920000, #6d0000, #490000, #240000, #000000,
You are welcome to look at the library's source code. :)
Correct function to generate array of colors!
function hex (c) {
var s = "0123456789abcdef";
var i = parseInt (c);
if (i == 0 || isNaN (c))
return "00";
i = Math.round (Math.min (Math.max (0, i), 255));
return s.charAt ((i - i % 16) / 16) + s.charAt (i % 16);
}
/* Convert an RGB triplet to a hex string */
function convertToHex (rgb) {
return hex(rgb[0]) + hex(rgb[1]) + hex(rgb[2]);
}
/* Remove '#' in color hex string */
function trim (s) { return (s.charAt(0) == '#') ? s.substring(1, 7) : s }
/* Convert a hex string to an RGB triplet */
function convertToRGB (hex) {
var color = [];
color[0] = parseInt ((trim(hex)).substring (0, 2), 16);
color[1] = parseInt ((trim(hex)).substring (2, 4), 16);
color[2] = parseInt ((trim(hex)).substring (4, 6), 16);
return color;
}
function generateColor(colorStart,colorEnd,colorCount){
// The beginning of your gradient
var start = convertToRGB (colorStart);
// The end of your gradient
var end = convertToRGB (colorEnd);
// The number of colors to compute
var len = colorCount;
//Alpha blending amount
var alpha = 0.0;
var saida = [];
for (i = 0; i < len; i++) {
var c = [];
alpha += (1.0/len);
c[0] = start[0] * alpha + (1 - alpha) * end[0];
c[1] = start[1] * alpha + (1 - alpha) * end[1];
c[2] = start[2] * alpha + (1 - alpha) * end[2];
saida.push(convertToHex (c));
}
return saida;
}
// Exemplo de como usar
var tmp = generateColor('#000000','#ff0ff0',10);
for (cor in tmp) {
$('#result_show').append("<div style='padding:8px;color:#FFF;background-color:#"+tmp[cor]+"'>COLOR "+cor+"° - #"+tmp[cor]+"</div>")
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="result_show"></div>
Yes, absolutely.
I do this in Java, should be fairly simple to do in JavaScript too.
First, you'll need to break the colors up into RGB components.
Then calculate the differences between start and finish of the components.
Finally, calculate percentage difference and multiply by the start color of each component, then add it to the start color.
Assuming you can get the RGB values, this should do it:
var diffRed = endColor.red - startColor.red;
var diffGreen = endColor.green - startColor.green;
var diffBlue = endColor.blue - startColor.blue;
diffRed = (diffRed * percentFade) + startColor.red;
diffGreen = (diffGreen * percentFade) + startColor.green;
diffBlue = (diffBlue * percentFade) + startColor.blue;
The "percentFade" is a floating decimal, signifying how far to fade into the "endColor". 1 would be a full fade (thus creating the end color). 0 would be no fade (the starting color).
I use this function based on #desau answer:
getGradientColor = function(start_color, end_color, percent) {
// strip the leading # if it's there
start_color = start_color.replace(/^\s*#|\s*$/g, '');
end_color = end_color.replace(/^\s*#|\s*$/g, '');
// convert 3 char codes --> 6, e.g. `E0F` --> `EE00FF`
if(start_color.length == 3){
start_color = start_color.replace(/(.)/g, '$1$1');
}
if(end_color.length == 3){
end_color = end_color.replace(/(.)/g, '$1$1');
}
// get colors
var start_red = parseInt(start_color.substr(0, 2), 16),
start_green = parseInt(start_color.substr(2, 2), 16),
start_blue = parseInt(start_color.substr(4, 2), 16);
var end_red = parseInt(end_color.substr(0, 2), 16),
end_green = parseInt(end_color.substr(2, 2), 16),
end_blue = parseInt(end_color.substr(4, 2), 16);
// calculate new color
var diff_red = end_red - start_red;
var diff_green = end_green - start_green;
var diff_blue = end_blue - start_blue;
diff_red = ( (diff_red * percent) + start_red ).toString(16).split('.')[0];
diff_green = ( (diff_green * percent) + start_green ).toString(16).split('.')[0];
diff_blue = ( (diff_blue * percent) + start_blue ).toString(16).split('.')[0];
// ensure 2 digits by color
if( diff_red.length == 1 ) diff_red = '0' + diff_red
if( diff_green.length == 1 ) diff_green = '0' + diff_green
if( diff_blue.length == 1 ) diff_blue = '0' + diff_blue
return '#' + diff_red + diff_green + diff_blue;
};
Example:
getGradientColor('#FF0000', '#00FF00', 0.4);
=> "#996600"
desau's answer is great. Here it is in javascript:
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 map(value, fromSource, toSource, fromTarget, toTarget) {
return (value - fromSource) / (toSource - fromSource) * (toTarget - fromTarget) + fromTarget;
}
function getColour(startColour, endColour, min, max, value) {
var startRGB = hexToRgb(startColour);
var endRGB = hexToRgb(endColour);
var percentFade = map(value, min, max, 0, 1);
var diffRed = endRGB.r - startRGB.r;
var diffGreen = endRGB.g - startRGB.g;
var diffBlue = endRGB.b - startRGB.b;
diffRed = (diffRed * percentFade) + startRGB.r;
diffGreen = (diffGreen * percentFade) + startRGB.g;
diffBlue = (diffBlue * percentFade) + startRGB.b;
var result = "rgb(" + Math.round(diffRed) + ", " + Math.round(diffGreen) + ", " + Math.round(diffBlue) + ")";
return result;
}
function changeBackgroundColour() {
var count = 0;
window.setInterval(function() {
count = (count + 1) % 200;
var newColour = getColour("#00FF00", "#FF0000", 0, 200, count);
document.body.style.backgroundColor = newColour;
}, 20);
}
changeBackgroundColour();
There is a JavaScript library which can create color gradients:
javascript-color-gradient
import Gradient from "javascript-color-gradient";
const colorGradient = new Gradient();
colorGradient.setGradient("#e6062d", "#408247"); // from red to green
colorGradient.setMidpoint(8); // set to 8 color steps
colorGradient.getArray(); // get all 8 colors: [ "#d11630", "#bd2534", ... ]
colorGradient.getColor(1); // #bd2534
Based on #drinor's answer - TypeScript support
const getGradientColor = (startColor: string, endColor: string, percent: number) => {
// strip the leading # if it's there
startColor = startColor.replace(/^\s*#|\s*$/g, '');
endColor = endColor.replace(/^\s*#|\s*$/g, '');
// convert 3 char codes --> 6, e.g. `E0F` --> `EE00FF`
if (startColor.length === 3) {
startColor = startColor.replace(/(.)/g, '$1$1');
}
if (endColor.length === 3) {
endColor = endColor.replace(/(.)/g, '$1$1');
}
// get colors
const startRed = parseInt(startColor.substr(0, 2), 16),
startGreen = parseInt(startColor.substr(2, 2), 16),
startBlue = parseInt(startColor.substr(4, 2), 16);
const endRed = parseInt(endColor.substr(0, 2), 16),
endGreen = parseInt(endColor.substr(2, 2), 16),
endBlue = parseInt(endColor.substr(4, 2), 16);
// calculate new color
let diffRed = endRed - startRed;
let diffGreen = endGreen - startGreen;
let diffBlue = endBlue - startBlue;
diffRed = ((diffRed * percent) + startRed);
diffGreen = ((diffGreen * percent) + startGreen);
diffBlue = ((diffBlue * percent) + startBlue);
let diffRedStr = diffRed.toString(16).split('.')[0];
let diffGreenStr = diffGreen.toString(16).split('.')[0];
let diffBlueStr = diffBlue.toString(16).split('.')[0];
// ensure 2 digits by color
if (diffRedStr.length === 1) diffRedStr = '0' + diffRedStr;
if (diffGreenStr.length === 1) diffGreenStr = '0' + diffGreenStr;
if (diffBlueStr.length === 1) diffBlueStr = '0' + diffBlueStr;
return '#' + diffRedStr + diffGreenStr + diffBlueStr;
}
example:
getGradientColor('#FF0000', '#00FF00', 0.4);
=> "#996600"
The xolor library has a gradient function. This will create an array with 8 colors in a gradient from a start color to an end color:
var gradientColors = []
var startColor = "rgb(100,200,50)", endColor = "green"
var start = xolor(startColor)
for(var n=0; n<8; n++) {
gradientColors.push(start.gradient(endColor, n/8))
}
See more on github: https://github.com/fresheneesz/xolor
chroma.js:
chroma.scale(['#fafa6e','#2A4858']).mode('lch').colors(6)
Not such mighty but in most cases working and you do not have to include any other libraries except jQuery for the following code:
HTML:
<div id="colors"></div>
JavaScript:
function rainbow(value, s, l, max, min, start, end) {
value = ((value - min) * (start - end) / max)+end;
return 'hsl(' + value + ','+s+'%,'+l+'%)';
}
function createRainbowDiv(start,end){
var gradient = $("<div>").css({display:"flex", "flex-direction":"row",height:"100%"});
for (var i = start; ((i <= end) && (i >= start)) || ((i >= end) && (i <= start));
i += (end-start) / Math.abs(end-start)){
gradient.append($("<div>").css({float:"left","background-color":rainbow(i, 100,50, Math.max(start,end), Math.min(start,end), start,end),flex:1}));
}
return gradient;
}
$("#colors").append(createRainbowDiv(0,150));
$("#colors").css("width","100%").css("height","10px");
This should make an div that contains a rainbow. See http://jsfiddle.net/rootandy/54rV7/
I needed to create a large enough array of color options for an unknown set of dynamic elements, but I needed each element to increment their way through a beginning color and an ending color. This sort of follows the "percent fade" approach except I had a difficult time following that logic. This is how I approached it using inputs of two rgb color values and calculating the number of elements on the page.
Here is a link to a codepen that demonstrates the concept.
Below is a code snippet of the problem.
<style>
#test {
width:200px;
height:100px;
border:solid 1px #000;
}
.test {
width:49%;
height:100px;
border:solid 1px #000;
display: inline-block;
}
</style>
</head>
<body>
<div id="test"></div>
<div class="test"></div>
<div class="test"></div>
<div class="test"></div>
<div class="test"></div>
<div class="test"></div>
<div class="test"></div>
<div class="test"></div>
<div class="test"></div>
<div class="test"></div>
<div class="test"></div>
<script>
var GColor = function(r,g,b) {
r = (typeof r === 'undefined')?0:r;
g = (typeof g === 'undefined')?0:g;
b = (typeof b === 'undefined')?0:b;
return {r:r, g:g, b:b};
};
// increases each channel by the difference of the two
// divided by 255 (the number of colors stored in the range array)
// but only stores a whole number
// This should respect any rgb combinations
// for start and end colors
var createColorRange = function(c1) {
var colorList = [], tmpColor, rr = 0, gg = 0, bb = 0;
for (var i=0; i<255; i++) {
tmpColor = new GColor();
if (rExp >= 0) {
tmpColor.r = Math.floor(c1.r - rr);
rr += rAdditive;
} else {
tmpColor.r = Math.floor(c1.r + rr);
rr += rAdditive;
}
if (gExp >= 0) {
tmpColor.g = Math.floor(c1.g - gg);
gg += gAdditive;
} else {
tmpColor.g = Math.floor(c1.g + gg);
gg += gAdditive;
}
if (bExp >= 0) {
tmpColor.b = Math.floor(c1.b - bb);
bb += bAdditive;
} else {
tmpColor.b = Math.floor(c1.b + bb);
bb += bAdditive;
}
console.log(tmpColor);
colorList.push(tmpColor);
}
return colorList;
};
/* ==================
Testing Code Below
================== */
var firstColor = new GColor(255, 24, 0);
var secondColor = new GColor(255, 182, 0);
// Determine the difference
var rExp = firstColor.r - secondColor.r;
// Divide that difference by length of the array
// you would like to create (255 in this case)
var rAdditive = Math.abs(rExp)/255;
var gExp = firstColor.g - secondColor.g;
var gAdditive = Math.abs(gExp)/255;
var bExp = firstColor.b - secondColor.b;
var bAdditive = Math.abs(bExp)/255;
var range = createColorRange(firstColor, secondColor);
console.log(range);
var pointer = 0;
// This gently cycles through
// all the colors on a single element
function rotateColors() {
var currentColor = range[pointer];
document.getElementById("test").style.backgroundColor = "rgb("+currentColor.r+","+currentColor.g+","+currentColor.b+")";
pointer++;
if (pointer < range.length) window.setTimeout(rotateColors, 5);
}
rotateColors();
// say I have 5 elements
// so I need 5 colors
// I already have my first and last colors
// but I need to locate the colors between
// my start color and my end color
// inside of this range
// so I divide the range's length by the
// number of colors I need
// and I store the index values of the middle values
// those index numbers will then act as my keys to retrieve those values
// and apply them to my element
var myColors = {};
var objects = document.querySelectorAll('.test');
myColors.num = objects.length;
var determineColors = function(numOfColors, colorArray) {
var colors = numOfColors;
var cRange = colorArray;
var distance = Math.floor(cRange.length/colors);
var object = document.querySelectorAll('.test');
var j = 0;
for (var i = 0; i < 255; i += distance) {
if ( (i === (distance*colors)) ) {
object[j].style.backgroundColor = "rgb(" + range[255].r + ", " + range[255].g + ", " + range[255].b + ")";
j = 0;
// console.log(range[i]);
} else {
// Apply to color to the element
object[j].style.backgroundColor = "rgb(" + range[i].r + ", " + range[i].g + ", " + range[i].b + ")";
// Have each element bleed into the next with a gradient
// object[j].style.background = "linear-gradient( 90deg, rgb(" + range[i].r + ", " + range[i].g + ", " + range[i].b + "), rgb(" + range[i+distance].r + ", " + range[i+distance].g + ", " + range[i+distance].b + "))";
j++;
}
}
};
setTimeout( determineColors(myColors.num, range), 2000);
</script>
</body>
You can retrieve the list of elements. I'm not familiar with jQuery, but prototypejs has Element.childElements() which will return an array. Once you know the length of the array, you can determine how much to change the pixel components for each step. Some of the following code I haven't tested out in the form I'm presenting it in, but it should hopefully give you an idea.
function hex (c) {
var s = "0123456789abcdef";
var i = parseInt (c);
if (i == 0 || isNaN (c))
return "00";
i = Math.round (Math.min (Math.max (0, i), 255));
return s.charAt ((i - i % 16) / 16) + s.charAt (i % 16);
}
/* Convert an RGB triplet to a hex string */
function convertToHex (rgb) {
return hex(rgb[0]) + hex(rgb[1]) + hex(rgb[2]);
}
/* Remove '#' in color hex string */
function trim (s) { return (s.charAt(0) == '#') ? s.substring(1, 7) : s }
/* Convert a hex string to an RGB triplet */
function convertToRGB (hex) {
var color[];
color[0] = parseInt ((trim(hex)).substring (0, 2), 16);
color[1] = parseInt ((trim(hex)).substring (2, 4), 16);
color[2] = parseInt ((trim(hex)).substring (4, 6), 16);
}
/* The start of your code. */
var start = convertToRGB ('#000000'); /* The beginning of your gradient */
var end = convertToRGB ('#ffffff'); /* The end of your gradient */
var arr = $('.gradientList').childElements();
var len = arr.length(); /* The number of colors to compute */
var alpha = 0.5; /* Alpha blending amount */
for (i = 0; i < len; i++) {
var c = [];
c[0] = start[0] * alpha + (1 - alpha) * end[0];
c[1] = start[1] * alpha + (1 - alpha) * end[1];
c[2] = start[2] * alpha + (1 - alpha) * end[2];
/* Set the background color of this element */
arr[i].setStyle ({ 'background-color': convertToHex (c) });
}
Basic Javascript - Background Gradient
Here's a ready-made function to set an elements background to be a gradient
Using CSS
Element.prototype.setGradient = function( from, to, vertical ){
this.style.background = 'linear-gradient(to '+(vertical ? 'top' : 'left')+', '+from+', '+to+' 100%)';
}
And Usage :
document.querySelector('.mydiv').setGradient('red','green');
This was tested working with chrome, I'll try to update for other browsers
Using Canvas
The most basic horizontal would be :
Element.prototype.setGradient = function( fromColor, toColor ){
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var b = this.getBoundingClientRect();
var grd = ctx.createLinearGradient(0, 0, b.width, 0);
canvas.width = b.width;
canvas.height = b.height;
grd.addColorStop(0, fromColor);
grd.addColorStop(1, toColor);
ctx.fillStyle = grd;
ctx.fillRect(0, 0, b.width, b.height);
this.style.backgroundImage = 'url('+canvas.toDataURL()+')';
}
And Usage :
document.querySelector('.mydiv').setGradient('red','green');
A Fiddle :
https://jsfiddle.net/jch39bey/
-
Adding Vertical Gradient
A simple flag to set vertical
Element.prototype.setGradient = function( fromColor, toColor, vertical ){
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var b = this.getBoundingClientRect();
var grd = ctx.createLinearGradient(0, 0, vertical ? 0 : b.width, vertical ? b.height : 0);
canvas.width = b.width;
canvas.height = b.height;
grd.addColorStop(0, fromColor);
grd.addColorStop(1, toColor);
ctx.fillStyle = grd;
ctx.fillRect(0, 0, b.width, b.height);
this.style.backgroundImage = 'url('+canvas.toDataURL()+')';
}
And Usage :
document.querySelector('.mydiv').setGradient('red','green',true);
Based on #desau's answer and some code from elsewhere, here's a jQuery step-by-step walkthrough:
function coloursBetween(fromColour, toColour, numberOfColours){
var colours = []; //holds output
var fromSplit = getRGBAValues(hexToRGBA(fromColour, 1.0)); //get raw values from hex
var toSplit = getRGBAValues(hexToRGBA(toColour, 1.0));
var fromRed = fromSplit[0]; //the red value as integer
var fromGreen = fromSplit[1];
var fromBlue = fromSplit[2];
var toRed = toSplit[0];
var toGreen = toSplit[1];
var toBlue = toSplit[2];
var difRed = toRed - fromRed; //difference between the two
var difGreen = toGreen - fromGreen;
var difBlue = toBlue - fromBlue;
var incrementPercentage = 1 / (numberOfColours-1); //how much to increment percentage by
for (var n = 0; n < numberOfColours; n++){
var percentage = n * incrementPercentage; //calculate percentage
var red = (difRed * percentage + fromRed).toFixed(0); //round em for legibility
var green = (difGreen * percentage + fromGreen).toFixed(0);
var blue = (difBlue * percentage + fromBlue).toFixed(0);
var colour = 'rgba(' + red + ',' + green + ',' + blue + ',1)'; //create string literal
colours.push(colour); //push home
}
return colours;
}
function getRGBAValues(string) {
var cleaned = string.substring(string.indexOf('(') +1, string.length-1);
var split = cleaned.split(",");
var intValues = [];
for(var index in split){
intValues.push(parseInt(split[index]));
}
return intValues;
}
function hexToRGBA(hex, alpha){
var c;
if(/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)){
c= hex.substring(1).split('');
if(c.length== 3){
c= [c[0], c[0], c[1], c[1], c[2], c[2]];
}
c= '0x'+c.join('');
return 'rgba('+[(c>>16)&255, (c>>8)&255, c&255].join(',')+','+alpha+')';
}
return rgba(0,0,0,1);
//throw new Error('Bad Hex');
}
There are three functions:
coloursBetween(fromColour, toColour, numberOfColours)
getRGBAValues(string)
hexToRGBA(hex, alpha)
Call the main function coloursBetween() passing in the starting colour and the ending colour, as well as the total number of colours you want to have returned. So if you request ten colours returned, you get the first from colour + 8 gradient colours + the final to colour.
The coloursBetween function starts by converting the incoming hex colours (e.g. #FFFFFF, #000000) into rgba (e.g. rgba(255,255,255,1) rgba(0,0,0,1)) and then subtracting the Red, Green and Blue values from each.
The difference between the Reds, Greens and Blues is then calculated. In this example it's -255 in each case. An increment is calculated and used to multiply new incremental values for the Red, Green and Blue. Alpha is always assumed to be one (full opacity). The new value is then added to the colours array and after the for loop has completed, it's returned.
Finally, call like this (going from Red to Blue):
var gradientColours = coloursBetween("#FF0000", "#0000FF", 5);
which you can use to for something like this:
Here's a script that does just what you're asking for:
https://gist.github.com/av01d/538b3fffc78fdc273894d173a83c563f
Very easy to use:
let colors;
colors = ColorSteps.getColorSteps('#000', 'rgba(255,0,0,0.1)', 10);
colors = ColorSteps.getColorSteps('red', 'blue', 5);
colors = ColorSteps.getColorSteps('hsl(180, 50%, 50%)', 'rgba(200,100,20,0.5)', 10);

Categories

Resources