Javascript Calculate darker colour - javascript

how can i change this calculation to a darker and not brighter color?
function increase_brightness(hex, percent){
// strip the leading # if it's there
hex = hex.replace(/^\s*#|\s*$/g, '');
// convert 3 char codes --> 6, e.g. `E0F` --> `EE00FF`
if(hex.length == 3){
hex = hex.replace(/(.)/g, '$1$1');
}
var r = parseInt(hex.substr(0, 2), 16),
g = parseInt(hex.substr(2, 2), 16),
b = parseInt(hex.substr(4, 2), 16);
return '#' +
((0|(1<<8) + r + (256 - r) * percent / 100).toString(16)).substr(1) +
((0|(1<<8) + g + (256 - g) * percent / 100).toString(16)).substr(1) +
((0|(1<<8) + b + (256 - b) * percent / 100).toString(16)).substr(1);
}
src = JavaScript Calculate brighter colour
demo http://jsbin.com/utavax/3/edit

You can change
return '#' +
((0|(1<<8) + r + (256 - r) * percent / 100).toString(16)).substr(1) +
((0|(1<<8) + g + (256 - g) * percent / 100).toString(16)).substr(1) +
((0|(1<<8) + b + (256 - b) * percent / 100).toString(16)).substr(1);
to
return '#' +
((0|(1<<8) + r * (1 - percent / 100)).toString(16)).substr(1) +
((0|(1<<8) + g * (1 - percent / 100)).toString(16)).substr(1) +
((0|(1<<8) + b * (1 - percent / 100).toString(16)).substr(1);
will fix your problem. demo.
But darker color is not a good definition. Darker can be interpreted as less brightness or less saturation. So the better approach is to convert the RGB color space to HSB color space, and tweak the S/B channels, then convert them back.
A little explanation on why negative value to original code is not OK.
Give -100 as percent, and some channel, say r, less than 128. Then
r + (256 - r) * percent / 100
is less than 0, after plus 1 << 8 = 256
((0|(1<<8) + r + (256 - r) * percent / 100)
is less than 256.
Take a number less than 256 will generate at most two hex digits by calling toString(16), so .substr(1) will contain 0 or 1 digit only. By combining all these wrong digits together will not generate a proper color in hex representation.

I updated your original function to do a cheap version of lightening/darkening by basically multiplying a percentage (up or down) off the original RGB values.
function adjust(hexInput: string, percent: number) {
let hex = hexInput;
// strip the leading # if it's there
hex = hex.replace(/^\s*#|\s*$/g, "");
// convert 3 char codes --> 6, e.g. `E0F` --> `EE00FF`
if (hex.length === 3) {
hex = hex.replace(/(.)/g, "$1$1");
}
let r = parseInt(hex.substr(0, 2), 16);
let g = parseInt(hex.substr(2, 2), 16);
let b = parseInt(hex.substr(4, 2), 16);
const calculatedPercent = (100 + percent) / 100;
r = Math.round(Math.min(255, Math.max(0, r * calculatedPercent)));
g = Math.round(Math.min(255, Math.max(0, g * calculatedPercent)));
b = Math.round(Math.min(255, Math.max(0, b * calculatedPercent)));
return `#${r.toString(16).toUpperCase()}${g.toString(16).toUpperCase()}${b
.toString(16)
.toUpperCase()}`;
}
console.log(adjust("#49D174", -14)) // Darken by 14% = #3FB464
console.log(adjust("#49D174", -27)) // Darken by 27% = #359955
The function takes the percent as an integer, but could easily be modified for a decimal. Negative to darken, positive to lighten.

I've taken the main idea from Stylus (1, 2) and a few code parts from Stack Overflow answers and produced a library, darken_color.js.
Here are a few usage examples:
darken('lightgreen', '0.1'); // -> #63e763
darken('lightgreen', '10'); // -> #63e763
darken('#90EE90', '10'); // -> #63e763
darken('#9e9', '10%'); // -> #98ed98

return '#' +
((0|(1<<8) + r + (256 - r) * percent / 100).toString(16)).substr(1) +
((0|(1<<8) + g + (256 - g) * percent / 100).toString(16)).substr(1) +
((0|(1<<8) + b + (256 - b) * percent / 100).toString(16)).substr(1);
Here you are adding some percentage of r, g and b to current values which makes the color lighter. If you use a negative value for percentage, it will decrease from current values and make the color darker.

Related

How to plot an ellipse on canvas from 2 points on the ellipse, where slope of major axis (rx), and minor axis (ry) length are unknown

This may be more of a mathematics problem, but maybe there is a simple javascript solution that I am missing.
I want to plot an ellipse on html canvas from user input of a center point, radius of the major (longest) axis, and 2 points will fall on the ellipse.
This should potentially create 2 possible ellipse paths, both of which will center around the center point, and cross through the 2 points.
So for example, if the center = [2, 1] major axis radius a = 10, point 1 u = [4, 2] and point 2 v = [5, 6], what is the minor axis radius b and angle of rotation theta?
So far I have tried to implement an equation that I found from https://math.stackexchange.com/questions/3210414/find-the-angle-of-rotation-and-minor-axis-length-of-ellipse-from-major-axis-leng,
but it does not return valid values. My javascript code looks like this:
function getEllipseFrom2Points(center, u, v, a) {
function getSlope(plusOrMinus) {
return Math.sqrt(((uy * vx - ux * vy) ** 2) / (-ux * uy * (a * (v2x + v2y) - 1) + vx * vy * (a * (u2x + u2y) - 1) - plusOrMinus * (uy * vx - ux * vy) * q) / (u2x * (1 - a * v2y) + v2x * (a * u2y - 1)));
}
function getMinorAxis(plusOrMinus) {
return (u2x + u2y + v2x + v2y - a * (2 * u2x * v2x + 2 * u2y * v2y + 2 * ux * uy * vx * vy + u2y * v2x + u2x * v2y) + plusOrMinus * 2 * (ux * vx + uy * vy) * q);
}
var vx = v[0],
vy = v[1],
ux = u[0],
uy = u[1],
v2x = vx ** 2,
v2y = vy ** 2,
u2x = ux ** 2,
u2y = uy ** 2,
q = Math.sqrt((1 - a * (u2x + u2y)) * (1 - a * (v2x + v2y))),
ellipse1 = { rx: a, ry: getMinorAxis(1), origin: center, rotation: getSlope(1) },
ellipse2 = { rx: a, ry: getMinorAxis(-1), origin: center, rotation: getSlope(-1) };
}
Either the equation that I am following is wrong, or I have implemented it wrong
In case anyone is interested, here is my solution to the problem, which isn't really "the" solution. If anyone can solve this I would still be happy to know.
Since I can't solve for both slope of the major axis and length of the minor axis, I just take a guess at slope and then test how close it is, and then refine the result by trying in a smaller and smaller region. Since the final ellipse that gets drawn is actually an estimation constructed from bezier curves, I can get close enough in a reasonable amount of time.
function getEllipseFrom2Points (center, u, v, a) {
function getSemiMinorAxis([x, y], a, t) {
// equation for rotated ellipse
// b = a(ycos(t) - xsin(t)) / sqrt(a^2 - x^2cos^2(t) - 2xysin(t)cos(t) - y^2sin^2(t)) and
// b = a(xsin(t) - ycos(t)) / sqrt(a^2 - x^2cos^2(t) - 2xysin(t)cos(t) - y^2sin^2(t))
// where a^2 !== (xcos(t) + ysin(t))^2
// and aycos(t) !== axsin(t)
if (a ** 2 !== (x * Math.cos(t) + y * Math.sin(t)) ** 2 &&
a * y * Math.cos(t) !== a * x * Math.sin(t)) {
var b = [],
q = (Math.sqrt(a ** 2 - x ** 2 * (Math.cos(t)) ** 2 - 2 * x * y * Math.sin(t) * Math.cos(t) - y ** 2 * (Math.sin(t)) ** 2));
b[0] = (a * (y * Math.cos(t) - x * Math.sin(t))) / q;
b[1] = (a * (x * Math.sin(t) - y * Math.cos(t))) / q;
return b;
}
}
function getAngle_radians(point1, point2){
return Math.atan2(point2[1] - point1[1], point2[0] - point1[0]);
}
function getDistance(point1, point2) {
return Math.sqrt((point2[0] - point1[0]) ** 2 + (point2[1] - point1[1]) ** 2);
}
function rotatePoint(point, center, radians) {
var x = (point[0] - center[0]) * Math.cos(radians) - (point[1] - center[1]) * Math.sin(radians) + center[0];
var y = (point[1] - center[1]) * Math.cos(radians) + (point[0] - center[0]) * Math.sin(radians) + center[1];
return [x, y];
}
function measure(ellipseRotation, pointOnEllipse, minorAxisLength) {
var d = getDistance(point, pointOnEllipse);
if (d < bestDistanceBetweenPointAndEllipse) {
bestDistanceBetweenPointAndEllipse = d;
bestEstimationOfB = minorAxisLength;
bestEstimationOfR = ellipseRotation;
}
}
function getBestEstimate(min, max) {
var testIncrement = (max - min) / 10;
for (let r = min; r < max; r = r + testIncrement) {
if (radPoint1 < r && radPoint2 < r || radPoint1 > r && radPoint2 > r) {//points both on same side of ellipse
semiMinorAxis = getSemiMinorAxis(v, a, r);
if (semiMinorAxis) {
for (let t = 0; t < circle; t = t + degree) {
ellipsePoint1 = [a * Math.cos(t), semiMinorAxis[0] * Math.sin(t)];
ellipsePoint2 = [a * Math.cos(t), semiMinorAxis[1] * Math.sin(t)];
point = rotatePoint(u, [0, 0], -r);
measure(r, ellipsePoint1, semiMinorAxis[0]);
measure(r, ellipsePoint2, semiMinorAxis[1]);
}
}
}
}
count++;
if (new Date().getTime() - startTime < 200 && count < 10) //refine estimate
getBestEstimate(bestEstimationOfR - testIncrement, bestEstimationOfR + testIncrement);
}
if (center instanceof Array &&
typeof center[0] === "number" &&
typeof center[1] === "number" &&
u instanceof Array &&
typeof u[0] === "number" &&
typeof u[1] === "number" &&
v instanceof Array &&
typeof v[0] === "number" &&
typeof v[1] === "number" &&
typeof a === "number") {
// translate points
u = [u[0] - center[0], u[1] - center[1]];
v = [v[0] - center[0], v[1] - center[1]];
var bestDistanceBetweenPointAndEllipse = a,
point,
semiMinorAxis,
ellipsePoint1,
ellipsePoint2,
bestEstimationOfB,
bestEstimationOfR,
radPoint1 = getAngle_radians([0, 0], v),
radPoint2 = getAngle_radians([0, 0], u),
circle = 2 * Math.PI,
degree = circle / 360,
startTime = new Date().getTime(),
count = 0;
getBestEstimate(0, circle);
var ellipseModel = MakerJs.$(new MakerJs.models.Ellipse(a, bestEstimationOfB))
.rotate(MakerJs.angle.toDegrees(bestEstimationOfR), [0, 0])
.move(center)
.originate([0, 0])
.$result;
return ellipseModel;
}

convert RGB color to XY

I have using RUCKUS API for changing color of lamp and I need to convert color from RGB to XY to call an API.
I have tried this code:
1) Get the RGB values from your color object and convert them to be between 0 and 1
function rgb_to_cie(red, green, blue)
{
//Apply a gamma correction to the RGB values, which makes the color more vivid and more the like the color displayed on the screen of your device
var red = (red > 0.04045) ? Math.pow((red + 0.055) / (1.0 + 0.055), 2.4) : (red / 12.92);
var green = (green > 0.04045) ? Math.pow((green + 0.055) / (1.0 + 0.055), 2.4) : (green / 12.92);
var blue = (blue > 0.04045) ? Math.pow((blue + 0.055) / (1.0 + 0.055), 2.4) : (blue / 12.92);
//RGB values to XYZ using the Wide RGB D65 conversion formula
var X = red * 0.664511 + green * 0.154324 + blue * 0.162028;
var Y = red * 0.283881 + green * 0.668433 + blue * 0.047685;
var Z = red * 0.000088 + green * 0.072310 + blue * 0.986039;
//Calculate the xy values from the XYZ values
var x = (X / (X + Y + Z)).toFixed(4);
var y = (Y / (X + Y + Z)).toFixed(4);
if (isNaN(x))
x = 0;
if (isNaN(y))
y = 0;
return [x, y];
}
but it didn't provide a proper solution.
SO if i pass r:182 g: 255 B: 65 as this then I got x as 22932 and y as 35249 (AS PER DOC OF API.)
How can I do that?
I got the solution and here I attached the answer if you're looing for,
let red = 100
let green = 100
let blue = 100
let redC = (red / 255)
let greenC = (green / 255)
let blueC = (blue / 255)
console.log(redC, greenC , blueC)
let redN = (redC > 0.04045) ? Math.pow((redC + 0.055) / (1.0 + 0.055), 2.4): (redC / 12.92)
let greenN = (greenC > 0.04045) ? Math.pow((greenC + 0.055) / (1.0 + 0.055), 2.4) : (greenC / 12.92)
let blueN = (blueC > 0.04045) ? Math.pow((blueC + 0.055) / (1.0 + 0.055), 2.4) : (blueC / 12.92)
console.log(redN, greenN, blueN)
let X = redN * 0.664511 + greenN * 0.154324 + blueN * 0.162028;
let Y = redN * 0.283881 + greenN * 0.668433 + blueN * 0.047685;
let Z = redN * 0.000088 + greenN * 0.072310 + blueN * 0.986039;
console.log(X, Y, Z)
let x = X / (X + Y + Z);
let y = Y / (X + Y + Z);
X = x * 65536
Y = y * 65536
There are few errors:
First: You are using the normal transformation formula, where red, green, and blue should be in the range 0.0 to 1.0. So you may need to divide the values by 255.
Second: you are applying gamma. To transform to X,Y,Z you need linear values. Normally we have gamma corrected RGB (e.g. pixel values). So you are overcorrecting. Instead, you should apply the inverse of gamma, so that you will get the linear R,G,B, so that you can apply linear transformation to get X,Y,Z.
Third: you are returning x,y and not X,Y, as your question (and comment). On the other hand, are you sure you need X,Y? Often x,y are used for "colour". I would expect a x,y + Y (or some brightness parameter).
Possibly you should scale back the values. As you see, often the range is 0 to 1, or 0 to 100 (in past), or 0 to 255, 0 to 65535. There is no standard: these are just numbers without a unit. [Contrary to computer screens, a Phillips Hue should know what it is the maximum brightness, but .. this is an exception].

Manipulate RGBA Javascript

In Javascript i have a Uint8Array() RGBA of image, here is console.log of this :
Here is image with rgba array as a string in HTML:
Is that possible to manipulate this array, to ex. change colors?
Thanks for help!
Here's a JS algorithm of plotting a pixel to this kind of array:
function changeColor(x, y, c)
{
colorArray[(x * 4) + (y * (imageWidth * 4))] = c.r;
colorArray[(x * 4) + (y * (imageWidth * 4)) + 1] = c.g;
colorArray[(x * 4) + (y * (imageWidth * 4)) + 2] = c.b;
colorArray[(x * 4) + (y * (imageWidth * 4)) + 3] = c.a;
}
where x and y are the coordinates of the pixel you want to change, imageWidth being the width of the image this array produces, c being the colour you want to change the pixel to, and colorArray is the array itself.

how to calculate the number to increment the variable

How can I calculate the missing number, so that when I add it to a certain variable the result will be equal to or greater than the other variable.
Promoter: 10
Detractor: 2
Total: 12
Average: 66.67
Target: 75
You need ??? Promoters to reach your Target.
I want to find how many Promoters I need if the Average is less than the Target. How can I calculate the missing number so that when I add it to the Promoter the results of the Average is equal to or greater than the Target.
Thank You!
function missingNum() {
var xPromoter = '';
var x = 10;
var y = 2;
var target = 75;
var z = x + y;
var v = ((x - y) / z) * 100;
average = Math.round(v * 100) / 100;
if (average<target) //how to increment x so that average => target
document.write("Promoter:" + "\n" + x + "<br>");
document.write("Detractor:" + "\n" + y + "<br>" );
document.write("Total:" + "\n" + z + "<br>" );
document.write("Average:" + "\n" + average + "<br>" );
document.write("Target:" + "\n" + target + "<br>" );
document.write("You need " + "\n" + xPromoter + "Promoters to reach your Target." );
}
This is a simple problem of algebra.
Average = ((Promoters - Detractors) / Total) * 100
Let fp be the final number of promotors you're looking for
Let d be the current number of Detractors
Let t be your target Average
The formula using these variable to find your target starts as such:
((fp - d) / (fp + d)) * 100 = t
Divide by 100
(fp - d) / (fp + d) = t / 100
Multiply both sides by (fp + d)
fp - d = (t / 100) * (fp + d)
Distribute t / 100
fp - d = (t / 100) * fp + (t / 100) * d
Add d to both sides
fp = (t / 100) * fp + (t / 100) * d + d
Subtract (t / 100) * x from both sides
fp - (t / 100) * fp = (t / 100) * d + d
Simplify the lefthand side
(1 - t / 100) * fp = (t / 100) * d + d
You want to have only fp on the lefthand side, so divide both sides by (1 - t / 100)
fp = ((t / 100) * d + d) / (1 - t / 100)
This is the formula you will use to find the total number of promotors you need
Now just plug in the numbers for t and d and solve for x
fp = ((75 / 100) * 2 + 2) / (1 - 75 / 100)
Writing this all out so you can see the final result:
fp = (.75 * 2 + 2) / (1 - .75)
fp = (1.5 + 2) / .25
fp = 3.5 / .25
fp = 14
Using the variables in your program
var fp = ((target / 100) * y + y) / (1 - target / 100);
xPromoter = fp - x;
Forget looping and do some math! Doing some algebra, the formula for needed promoters, given the target and current number of detractors and promoters, is:
promotersNeeded = (detractors * (target + 100) / (100 - target)) - promoters
So in your example,
promotersNeeded = 2 * (75 + 100) / (100 - 75) - 10
which comes out to 4.
You could always make a simple function.. however a calculation might be better..
function calculateNeededPromoters (promo, detrac) {
var percentage = 0.00;
var currentPromo = promo;
while (percentage < 75) {
currentPromo++;
var total = currentPromo + detrac;
var v = ((currentPromo - detrac) / total) * 100;
percentage = Math.round(v * 100) / 100;
}
return currentPromo;
}

JavaScript Calculate brighter colour

I have a colour value in JS as a string
#ff0000
How would I go about programatically calculating a brighter/lighter version of this colour, for example #ff4848, and be able to calculate the brightness via a percentage, e.g.
increase_brightness('#ff0000', 50); // would make it 50% brighter
function increase_brightness(hex, percent){
// strip the leading # if it's there
hex = hex.replace(/^\s*#|\s*$/g, '');
// convert 3 char codes --> 6, e.g. `E0F` --> `EE00FF`
if(hex.length == 3){
hex = hex.replace(/(.)/g, '$1$1');
}
var r = parseInt(hex.substr(0, 2), 16),
g = parseInt(hex.substr(2, 2), 16),
b = parseInt(hex.substr(4, 2), 16);
return '#' +
((0|(1<<8) + r + (256 - r) * percent / 100).toString(16)).substr(1) +
((0|(1<<8) + g + (256 - g) * percent / 100).toString(16)).substr(1) +
((0|(1<<8) + b + (256 - b) * percent / 100).toString(16)).substr(1);
}
/**
* ('#000000', 50) --> #808080
* ('#EEEEEE', 25) --> #F2F2F2
* ('EEE , 25) --> #F2F2F2
**/
Update
#zyklus's answer is simpler and has the same effect. Please refer to this answer only if you are interested in converting between RGB and HSL.
To set the brightness of RGB:
Convert RGB to HSL
Set the brightness of HSL
Convert back from HSL to RGB
This link used to have code to convert RGB to HSL and reverse:
http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
/**
* Converts an RGB color value to HSL. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes r, g, and b are contained in the set [0, 255] and
* returns h, s, and l in the set [0, 1].
*
* #param Number r The red color value
* #param Number g The green color value
* #param Number b The blue color value
* #return Array The HSL representation
*/
function rgbToHsl(r, g, b){
r /= 255, g /= 255, b /= 255;
var max = Math.max(r, g, b), min = Math.min(r, g, b);
var h, s, l = (max + min) / 2;
if(max == min){
h = s = 0; // achromatic
}else{
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch(max){
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return [h, s, l];
}
/**
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes h, s, and l are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
*
* #param Number h The hue
* #param Number s The saturation
* #param Number l The lightness
* #return Array The RGB representation
*/
function hslToRgb(h, s, l){
var r, g, b;
if(s == 0){
r = g = b = l; // achromatic
}else{
function hue2rgb(p, q, t){
if(t < 0) t += 1;
if(t > 1) t -= 1;
if(t < 1/6) return p + (q - p) * 6 * t;
if(t < 1/2) return q;
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return [r * 255, g * 255, b * 255];
}
I made some example with it. Check this link: http://jsfiddle.net/sangdol/euSLy/4/
And this is the increase_brightness() function:
function increase_brightness(rgbcode, percent) {
var r = parseInt(rgbcode.slice(1, 3), 16),
g = parseInt(rgbcode.slice(3, 5), 16),
b = parseInt(rgbcode.slice(5, 7), 16),
HSL = rgbToHsl(r, g, b),
newBrightness = HSL[2] + HSL[2] * (percent / 100),
RGB;
RGB = hslToRgb(HSL[0], HSL[1], newBrightness);
rgbcode = '#'
+ convertToTwoDigitHexCodeFromDecimal(RGB[0])
+ convertToTwoDigitHexCodeFromDecimal(RGB[1])
+ convertToTwoDigitHexCodeFromDecimal(RGB[2]);
return rgbcode;
}
function convertToTwoDigitHexCodeFromDecimal(decimal){
var code = Math.round(decimal).toString(16);
(code.length > 1) || (code = '0' + code);
return code;
}
You can pass a negative value as a percent argument to make it darken.
In case anyone needs it, I converted the color brightness JavaScript code to ASP / VBScript for a project and thought I would share it with you:
'::Color Brightness (0-100)
'ex. ColorBrightness("#FF0000",25) 'Darker
'ex. ColorBrightness("#FF0000",50) 'Mid
'ex. ColorBrightness("#FF0000",75) 'Lighter
Function ColorBrightness(strRGB,intBrite)
strRGB = Replace(strRGB,"#","")
r = CInt("&h" & Mid(strRGB,1,2))
g = CInt("&h" & Mid(strRGB,3,2))
b = CInt("&h" & Mid(strRGB,5,2))
arrHSL = RGBtoHSL(r, g, b)
dblOrigBrite = CDbl(arrHSL(2) * 100)
arrRGB = HSLtoRGB(arrHSL(0), arrHSL(1), intBrite/100)
newRGB = "#" & HEXtoDEC(arrRGB(0)) & HEXtoDEC(arrRGB(1)) & HEXtoDEC(arrRGB(2))
ColorBrightness = newRGB
End Function
'::RGB to HSL Function
Function RGBtoHSL(r,g,b)
r = CDbl(r/255)
g = CDbl(g/255)
b = CDbl(b/255)
max = CDbl(MaxCalc(r & "," & g & "," & b))
min = CDbl(MinCalc(r & "," & g & "," & b))
h = CDbl((max + min) / 2)
s = CDbl((max + min) / 2)
l = CDbl((max + min) / 2)
If max = min Then
h = 0
s = 0
Else
d = max - min
s = IIf(l > 0.5, d / (2 - max - min), d / (max + min))
Select Case CStr(max)
Case CStr(r)
h = (g - b) / d + (IIf(g < b, 6, 0))
Case CStr(g)
h = (b - r) / d + 2
Case CStr(b)
h = (r - g) / d + 4
End Select
h = h / 6
End If
RGBtoHSL = Split(h & "," & s & "," & l, ",")
End Function
'::HSL to RGB Function
Function HSLtoRGB(h,s,l)
If s = 0 Then
r = l
g = l
b = l
Else
q = IIf(l < 0.5, l * (1 + s), l + s - l * s)
p = 2 * l - q
r = HUEtoRGB(p, q, h + 1/3)
g = HUEtoRGB(p, q, h)
b = HUEtoRGB(p, q, h - 1/3)
End If
HSLtoRGB = Split(r * 255 & "," & g * 255 & "," & b * 255, ",")
End Function
'::Hue to RGB Function
Function HUEtoRGB(p,q,t)
If CDbl(t) < 0 Then t = t + 1
If CDbl(t) > 1 Then t = t - 1
If CDbl(t) < (1/6) Then
HUEtoRGB = p + (q - p) * 6 * t
Exit Function
End If
If CDbl(t) < (1/2) Then
HUEtoRGB = q
Exit Function
End If
If CDbl(t) < (2/3) Then
HUEtoRGB = p + (q - p) * (2/3 - t) * 6
Exit Function
End If
HUEtoRGB = p
End Function
'::Hex to Decimal Function
Function HEXtoDEC(d)
h = Hex(Round(d,0))
h = Right(String(2,"0") & h,2)
HEXtoDEC = h
End Function
'::Max Function
Function MaxCalc(valList)
valList = Split(valList,",")
b = 0
For v = 0 To UBound(valList)
a = valList(v)
If CDbl(a) > CDbl(b) Then b = a
Next
MaxCalc = b
End Function
'::Min Function
Function MinCalc(valList)
valList = Split(valList,",")
For v = 0 To UBound(valList)
a = valList(v)
If b = "" Then b = a
If CDbl(a) < CDbl(b) AND b <> "" Then b = a
Next
MinCalc = b
End Function
'::IIf Emulation Function
Function IIf(condition,conTrue,conFalse)
If (condition) Then
IIf = conTrue
Else
IIf = conFalse
End If
End Function
That way you won't need any conversion of the source color.
check out this fiddle : https://jsfiddle.net/4c47otou/
increase_brightness = function(color,percent){
var ctx = document.createElement('canvas').getContext('2d');
ctx.fillStyle = color;
ctx.fillRect(0,0,1,1);
var color = ctx.getImageData(0,0,1,1);
var r = color.data[0] + Math.floor( percent / 100 * 255 );
var g = color.data[1] + Math.floor( percent / 100 * 255 );
var b = color.data[2] + Math.floor( percent / 100 * 255 );
return 'rgb('+r+','+g+','+b+')';
}
Example usage :
increase_brightness('#0000ff',20);
increase_brightness('khaki',20);
increase_brightness('rgb(12, 7, 54)',20);
// color is a hex color like #aaaaaa and percent is a float, 1.00=100%
// increasing a color by 50% means a percent value of 1.5
function brighten(color, percent) {
var r=parseInt(color.substr(1,2),16);
var g=parseInt(color.substr(3,2),16);
var b=parseInt(color.substr(5,2),16);
return '#'+
Math.min(255,Math.floor(r*percent)).toString(16)+
Math.min(255,Math.floor(g*percent)).toString(16)+
Math.min(255,Math.floor(b*percent)).toString(16);
}
Live sample: http://jsfiddle.net/emM55/
Here is the increaseBrightness function with the RGB->HSL->RGB method. "amount" should be in percent.
HSL<->RGB conversion functions taken from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
function increaseBrightness( color, amount ) {
var r = parseInt(color.substr(1, 2), 16);
var g = parseInt(color.substr(3, 2), 16);
var b = parseInt(color.substr(5, 2), 16);
hsl = rgbToHsl( r, g, b );
hsl.l += hsl.l + (amount / 100);
if( hsl.l > 1 ) hsl.l = 1;
rgb = hslToRgb( hsl.h, hsl.s, hsl.l );
var v = rgb.b | (rgb.g << 8) | (rgb.r << 16);
return '#' + v.toString(16);
}
function rgbToHsl(r, g, b){
r /= 255, g /= 255, b /= 255;
var max = Math.max(r, g, b), min = Math.min(r, g, b);
var h, s, l = (max + min) / 2;
if(max == min){
h = s = 0; // achromatic
}else{
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch(max){
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return {'h':h, 's':s, 'l':l};
}
function hslToRgb(h, s, l){
var r, g, b;
if(s == 0){
r = g = b = l; // achromatic
}else{
function hue2rgb(p, q, t){
if(t < 0) t += 1;
if(t > 1) t -= 1;
if(t < 1/6) return p + (q - p) * 6 * t;
if(t < 1/2) return q;
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return { 'r':r * 255, 'g':g * 255, 'b':b * 255 };
}
I found a variation of Sanghyun Lee's reply generates the best result.
Convert RGB to HSL
Set the brightness of HSL
Convert back from HSLto RGB
The difference/variation is how you increase/decrease the brightness.
newBrightness = HSL[2] + HSL[2] * (percent / 100) // Original code
Instead of applying a percentage on the current brightness, it works better if it is treated as absolute increment/decrement. Since the luminosity range is 0 to 1, the percent can be applied on the whole range (1 - 0) * percent/100.
newBrightness = HSL[2] + (percent / 100);
newBrightness = Math.max(0, Math.min(1, newBrightness));
Another nice property of this approach is increment & decrement negate each other.
Image below shows darker and lighter colors with 5% increment. Note, how the palette is reasonably smooth and often ends with black and white.
Palette with original approach - gets stuck at certain colors.
I know this an old question, but I found no answer that simply manipulates css hsl color. I found the old answers here to be too complex and slow, even producing poor results, so a different approach seems warranted. The following alternative is much more performant and less complex.
Of course, this answer requires you to use hsl colors throughout your app, otherwise you still have to do a bunch of conversions! Though, if you need to manipulate brightness eg in a game loop, you should be using hsl values anyway as they are much better suited for programmatic manipulation. The only drawback with hsl from rgb as far as I can tell, is that it's harder to "read" what hue you're seeing like you can with rgb strings.
function toHslArray(hslCss) {
let sep = hslCss.indexOf(",") > -1 ? "," : " "
return hslCss.substr(4).split(")")[0].split(sep)
}
function adjustHslBrightness(color, percent) {
let hsl = toHslArray(color)
return "hsl(" + hsl[0] + "," + hsl[1] + ", " + (percent + "%") + ")"
}
let hsl = "hsl(200, 40%, 40%)"
let hsl2 = adjustHslBrightness(hsl, 80)
function brighten(color, c) {
const calc = (sub1,sub2)=> Math.min(255,Math.floor(parseInt(color.substr(sub1,sub2),16)*c)).toString(16).padStart(2,"0")
return `#${calc(1,2)}${calc(3,2)}${calc(5,2)}`
}
const res = brighten("#23DA4C", .5) // "#116d26"
console.log(res)
What I use:
//hex can be string or number
//rate: 1 keeps the color same. < 1 darken. > 1 lighten.
//to_string: set to true if you want the return value in string
function change_brightness(hex, rate, to_string = false) {
if (typeof hex === 'string') {
hex = hex.replace(/^\s*#|\s*$/g, '');
} else {
hex = hex.toString(16);
}
if (hex.length == 3) {
hex = hex.replace(/(.)/g, '$1$1');
} else {
hex = ("000000" + hex).slice(-6);
}
let r = parseInt(hex.substr(0, 2), 16);
let g = parseInt(hex.substr(2, 2), 16);
let b = parseInt(hex.substr(4, 2), 16);
let h, s, v;
[h, s, v] = rgb2hsv(r, g, b);
v = parseInt(v * rate);
[r, g, b] = hsv2rgb(h, s, v);
hex = ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
if (to_string) return "#" + hex;
return parseInt(hex, 16);
}
function rgb2hsv(r,g,b) {
let v = Math.max(r,g,b), n = v-Math.min(r,g,b);
let h = n && ((v === r) ? (g-b)/n : ((v === g) ? 2+(b-r)/n : 4+(r-g)/n));
return [60*(h<0?h+6:h), v&&n/v, v];
}
function hsv2rgb(h,s,v) {
let f = (n,k=(n+h/60)%6) => v - v*s*Math.max( Math.min(k,4-k,1), 0);
return [f(5),f(3),f(1)];
}
A variant with lodash:
// color('#EBEDF0', 30)
color(hex, percent) {
return '#' + _(hex.replace('#', '')).chunk(2)
.map(v => parseInt(v.join(''), 16))
.map(v => ((0 | (1 << 8) + v + (256 - v) * percent / 100).toString(16))
.substr(1)).join('');
}
First get a quick understanding of hex color codes.
Then it should be pretty easy to break down your color value into RGB, make the adjustments and then return the new color code.

Categories