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);