Programmatically Lighten or Darken a hex color (or rgb, and blend colors)
Well, this answer has become its own beast. Many new versions, it was getting stupid long. Many thanks to all of the great many contributors to this answer. But, in order to keep it simple for the masses. I archived all the versions/history of this answer's evolution to my github. And started it over clean on StackOverflow here with the newest version. A special thanks goes out to Mike 'Pomax' Kamermans for this version. He gave me the new math.
This function (pSBC
) will take a HEX or RGB web color. pSBC
can shade it darker or lighter, or blend it with a second color, and can also pass it right thru but convert from Hex to RGB (Hex2RGB) or RGB to Hex (RGB2Hex). All without you even knowing what color format you are using.
This runs really fast, probably the fastest, especially considering its many features. It was a long time in the making. See the whole story on my github. If you want the absolutely smallest and fastest possible way to shade or blend, see the Micro Functions below and use one of the 2-liner speed demons. They are great for intense animations, but this version here is fast enough for most animations.
This function uses Log Blending or Linear Blending. However, it does NOT convert to HSL to properly lighten or darken a color. Therefore, results from this function will differ from those much larger and much slower functions that use HSL.
jsFiddle with pSBC
github > pSBC Wiki
Features:
- Auto-detects and accepts standard Hex colors in the form of strings. For example:
"#AA6622"
or"#bb551144"
. - Auto-detects and accepts standard RGB colors in the form of strings. For example:
"rgb(123,45,76)"
or"rgba(45,15,74,0.45)"
. - Shades colors to white or black by percentage.
- Blends colors together by percentage.
- Does Hex2RGB and RGB2Hex conversion at the same time, or solo.
- Accepts 3 digit (or 4 digit w/ alpha) HEX color codes, in the form #RGB (or #RGBA). It will expand them. For Example:
"#C41"
becomes"#CC4411"
. - Accepts and (Linear) blends alpha channels. If either the
c0
(from) color or thec1
(to) color has an alpha channel, then the returned color will have an alpha channel. If both colors have an alpha channel, then the returned color will be a linear blend of the two alpha channels using the percentage given (just as if it were a normal color channel). If only one of the two colors has an alpha channel, this alpha will just be passed thru to the returned color. This allows one to blend/shade a transparent color while maintaining the transparency level. Or, if the transparency levels should blend as well, make sure both colors have alphas. When shading, it will pass the alpha channel straight thru. If you want basic shading that also shades the alpha channel, then usergb(0,0,0,1)
orrgb(255,255,255,1)
as yourc1
(to) color (or their hex equivalents). For RGB colors, the returned color's alpha channel will be rounded to 3 decimal places. - RGB2Hex and Hex2RGB conversions are implicit when using blending. Regardless of the
c0
(from) color; the returned color will always be in the color format of thec1
(to) color, if one exists. If there is noc1
(to) color, then pass'c'
in as thec1
color and it will shade and convert whatever thec0
color is. If conversion only is desired, then pass0
in as the percentage (p
) as well. If thec1
color is omitted or a non-string
is passed in, it will not convert. - A secondary function is added to the global as well.
pSBCr
can be passed a Hex or RGB color and it returns an object containing this color information. Its in the form: {r: XXX, g: XXX, b: XXX, a: X.XXX}. Where.r
,.g
, and.b
have range 0 to 255. And when there is no alpha:.a
is -1. Otherwise:.a
has range 0.000 to 1.000. - For RGB output, it outputs
rgba()
overrgb()
when a color with an alpha channel was passed intoc0
(from) and/orc1
(to). - Minor Error Checking has been added. It's not perfect. It can still crash or create jibberish. But it will catch some stuff. Basically, if the structure is wrong in some ways or if the percentage is not a number or out of scope, it will return
null
. An example:pSBC(0.5,"salt") == null
, where as it thinks#salt
is a valid color. Delete the four lines which end withreturn null;
to remove this feature and make it faster and smaller. - Uses Log Blending. Pass
true
in forl
(the 4th parameter) to use Linear Blending.
Code:
// Version 4.0
const pSBC=(p,c0,c1,l)=>{
let r,g,b,P,f,t,h,i=parseInt,m=Math.round,a=typeof(c1)=="string";
if(typeof(p)!="number"||p<-1||p>1||typeof(c0)!="string"||(c0[0]!='r'&&c0[0]!='#')||(c1&&!a))return null;
if(!this.pSBCr)this.pSBCr=(d)=>{
let n=d.length,x={};
if(n>9){
[r,g,b,a]=d=d.split(","),n=d.length;
if(n<3||n>4)return null;
x.r=i(r[3]=="a"?r.slice(5):r.slice(4)),x.g=i(g),x.b=i(b),x.a=a?parseFloat(a):-1
}else{
if(n==8||n==6||n<4)return null;
if(n<6)d="#"+d[1]+d[1]+d[2]+d[2]+d[3]+d[3]+(n>4?d[4]+d[4]:"");
d=i(d.slice(1),16);
if(n==9||n==5)x.r=d>>24&255,x.g=d>>16&255,x.b=d>>8&255,x.a=m((d&255)/0.255)/1000;
else x.r=d>>16,x.g=d>>8&255,x.b=d&255,x.a=-1
}return x};
h=c0.length>9,h=a?c1.length>9?true:c1=="c"?!h:false:h,f=this.pSBCr(c0),P=p<0,t=c1&&c1!="c"?this.pSBCr(c1):P?{r:0,g:0,b:0,a:-1}:{r:255,g:255,b:255,a:-1},p=P?p*-1:p,P=1-p;
if(!f||!t)return null;
if(l)r=m(P*f.r+p*t.r),g=m(P*f.g+p*t.g),b=m(P*f.b+p*t.b);
else r=m((P*f.r**2+p*t.r**2)**0.5),g=m((P*f.g**2+p*t.g**2)**0.5),b=m((P*f.b**2+p*t.b**2)**0.5);
a=f.a,t=t.a,f=a>=0||t>=0,a=f?a<0?t:t<0?a:a*P+t*p:0;
if(h)return"rgb"+(f?"a(":"(")+r+","+g+","+b+(f?","+m(a*1000)/1000:"")+")";
else return"#"+(4294967296+r*16777216+g*65536+b*256+(f?m(a*255):0)).toString(16).slice(1,f?undefined:-2)
}
Usage:
// Setup:
let color1 = "rgb(20,60,200)";
let color2 = "rgba(20,60,200,0.67423)";
let color3 = "#67DAF0";
let color4 = "#5567DAF0";
let color5 = "#F3A";
let color6 = "#F3A9";
let color7 = "rgb(200,60,20)";
let color8 = "rgba(200,60,20,0.98631)";
// Tests:
/*** Log Blending ***/
// Shade (Lighten or Darken)
pSBC ( 0.42, color1 ); // rgb(20,60,200) + [42% Lighter] => rgb(166,171,225)
pSBC ( -0.4, color5 ); // #F3A + [40% Darker] => #c62884
pSBC ( 0.42, color8 ); // rgba(200,60,20,0.98631) + [42% Lighter] => rgba(225,171,166,0.98631)
// Shade with Conversion (use "c" as your "to" color)
pSBC ( 0.42, color2, "c" ); // rgba(20,60,200,0.67423) + [42% Lighter] + [Convert] => #a6abe1ac
// RGB2Hex & Hex2RGB Conversion Only (set percentage to zero)
pSBC ( 0, color6, "c" ); // #F3A9 + [Convert] => rgba(255,51,170,0.6)
// Blending
pSBC ( -0.5, color2, color8 ); // rgba(20,60,200,0.67423) + rgba(200,60,20,0.98631) + [50% Blend] => rgba(142,60,142,0.83)
pSBC ( 0.7, color2, color7 ); // rgba(20,60,200,0.67423) + rgb(200,60,20) + [70% Blend] => rgba(168,60,111,0.67423)
pSBC ( 0.25, color3, color7 ); // #67DAF0 + rgb(200,60,20) + [25% Blend] => rgb(134,191,208)
pSBC ( 0.75, color7, color3 ); // rgb(200,60,20) + #67DAF0 + [75% Blend] => #86bfd0
/*** Linear Blending ***/
// Shade (Lighten or Darken)
pSBC ( 0.42, color1, false, true ); // rgb(20,60,200) + [42% Lighter] => rgb(119,142,223)
pSBC ( -0.4, color5, false, true ); // #F3A + [40% Darker] => #991f66
pSBC ( 0.42, color8, false, true ); // rgba(200,60,20,0.98631) + [42% Lighter] => rgba(223,142,119,0.98631)
// Shade with Conversion (use "c" as your "to" color)
pSBC ( 0.42, color2, "c", true ); // rgba(20,60,200,0.67423) + [42% Lighter] + [Convert] => #778edfac
// RGB2Hex & Hex2RGB Conversion Only (set percentage to zero)
pSBC ( 0, color6, "c", true ); // #F3A9 + [Convert] => rgba(255,51,170,0.6)
// Blending
pSBC ( -0.5, color2, color8, true ); // rgba(20,60,200,0.67423) + rgba(200,60,20,0.98631) + [50% Blend] => rgba(110,60,110,0.83)
pSBC ( 0.7, color2, color7, true ); // rgba(20,60,200,0.67423) + rgb(200,60,20) + [70% Blend] => rgba(146,60,74,0.67423)
pSBC ( 0.25, color3, color7, true ); // #67DAF0 + rgb(200,60,20) + [25% Blend] => rgb(127,179,185)
pSBC ( 0.75, color7, color3, true ); // rgb(200,60,20) + #67DAF0 + [75% Blend] => #7fb3b9
/*** Other Stuff ***/
// Error Checking
pSBC ( 0.42, "#FFBAA" ); // #FFBAA + [42% Lighter] => null (Invalid Input Color)
pSBC ( 42, color1, color5 ); // rgb(20,60,200) + #F3A + [4200% Blend] => null (Invalid Percentage Range)
pSBC ( 0.42, {} ); // [object Object] + [42% Lighter] => null (Strings Only for Color)
pSBC ( "42", color1 ); // rgb(20,60,200) + ["42"] => null (Numbers Only for Percentage)
pSBC ( 0.42, "salt" ); // salt + [42% Lighter] => null (A Little Salt is No Good...)
// Error Check Fails (Some Errors are not Caught)
pSBC ( 0.42, "#salt" ); // #salt + [42% Lighter] => #a5a5a500 (...and a Pound of Salt is Jibberish)
// Ripping
pSBCr ( color4 ); // #5567DAF0 + [Rip] => [object Object] => {'r':85,'g':103,'b':218,'a':0.941}
The picture below will help show the difference in the two blending methods:
Micro Functions
If you really want speed and size, you will have to use RGB not HEX. RGB is more straightforward and simple, HEX writes too slow and comes in too many flavors for a simple two-liner (IE. it could be a 3, 4, 6, or 8 digit HEX code). You will also need to sacrifice some features, no error checking, no HEX2RGB nor RGB2HEX. As well, you will need to choose a specific function (based on its function name below) for the color blending math, and if you want shading or blending. These functions do support alpha channels. And when both input colors have alphas it will Linear Blend them. If only one of the two colors has an alpha, it will pass it straight thru to the resulting color. Below are two liner functions that are incredibly fast and small:
const RGB_Linear_Blend=(p,c0,c1)=>{
var i=parseInt,r=Math.round,P=1-p,[a,b,c,d]=c0.split(","),[e,f,g,h]=c1.split(","),x=d||h,j=x?","+(!d?h:!h?d:r((parseFloat(d)*P+parseFloat(h)*p)*1000)/1000+")"):")";
return"rgb"+(x?"a(":"(")+r(i(a[3]=="a"?a.slice(5):a.slice(4))*P+i(e[3]=="a"?e.slice(5):e.slice(4))*p)+","+r(i(b)*P+i(f)*p)+","+r(i(c)*P+i(g)*p)+j;
}
const RGB_Linear_Shade=(p,c)=>{
var i=parseInt,r=Math.round,[a,b,c,d]=c.split(","),P=p<0,t=P?0:255*p,P=P?1+p:1-p;
return"rgb"+(d?"a(":"(")+r(i(a[3]=="a"?a.slice(5):a.slice(4))*P+t)+","+r(i(b)*P+t)+","+r(i(c)*P+t)+(d?","+d:")");
}
const RGB_Log_Blend=(p,c0,c1)=>{
var i=parseInt,r=Math.round,P=1-p,[a,b,c,d]=c0.split(","),[e,f,g,h]=c1.split(","),x=d||h,j=x?","+(!d?h:!h?d:r((parseFloat(d)*P+parseFloat(h)*p)*1000)/1000+")"):")";
return"rgb"+(x?"a(":"(")+r((P*i(a[3]=="a"?a.slice(5):a.slice(4))**2+p*i(e[3]=="a"?e.slice(5):e.slice(4))**2)**0.5)+","+r((P*i(b)**2+p*i(f)**2)**0.5)+","+r((P*i(c)**2+p*i(g)**2)**0.5)+j;
}
const RGB_Log_Shade=(p,c)=>{
var i=parseInt,r=Math.round,[a,b,c,d]=c.split(","),P=p<0,t=P?0:p*255**2,P=P?1+p:1-p;
return"rgb"+(d?"a(":"(")+r((P*i(a[3]=="a"?a.slice(5):a.slice(4))**2+t)**0.5)+","+r((P*i(b)**2+t)**0.5)+","+r((P*i(c)**2+t)**0.5)+(d?","+d:")");
}
Want more info? Read the full writeup on github.
PT
(P.s. If anyone has the math for another blending method, please share.)
Programmatically Lighten or Darken a hex color in lua - nvim highlight colors
I wouldn't resort to bit ops in Lua 5.2 and lower, especially as Lua 5.1 lacks them (LuaJIT however does provide them); use multiplication, floor division & mod instead, and take care to clamp your values:
local function clamp(component)
return math.min(math.max(component, 0), 255)
end
function LightenDarkenColor(col, amt)
local num = tonumber(col, 16)
local r = math.floor(num / 0x10000) + amt
local g = (math.floor(num / 0x100) % 0x100) + amt
local b = (num % 0x100) + amt
return string.format("%#x", clamp(r) * 0x10000 + clamp(g) * 0x100 + clamp(b))
end
Programmatically Lighten a Color
I would go for the second option. Generally speaking the RGB space is not really good for doing color manipulation (creating transition from one color to an other, lightening / darkening a color, etc). Below are two sites I've found with a quick search to convert from/to RGB to/from HSL:
- from the "Fundamentals of Computer Graphics"
- some sourcecode in C# - should be easy to adapt to other programming languages.
Programmatically Lighten or Darken a hex color in dart
For people who want to darken or lighten Color
instead of hex string
// ranges from 0.0 to 1.0
Color darken(Color color, [double amount = .1]) {
assert(amount >= 0 && amount <= 1);
final hsl = HSLColor.fromColor(color);
final hslDark = hsl.withLightness((hsl.lightness - amount).clamp(0.0, 1.0));
return hslDark.toColor();
}
Color lighten(Color color, [double amount = .1]) {
assert(amount >= 0 && amount <= 1);
final hsl = HSLColor.fromColor(color);
final hslLight = hsl.withLightness((hsl.lightness + amount).clamp(0.0, 1.0));
return hslLight.toColor();
}
// usage
final lightRed = lighten(Colors.red);
final darkBlue = darken(Colors.blue, .3);
Live Demo
Programmatically darken a Hex colour
If you're not bothered about too much control, and just want a generally darker version of a colour, then:
col = (col & 0xfefefe) >> 1;
Is a nice quick way to halve a colour value (assuming it's packed as a byte per channel, obviously).
In the same way brighter would be:
col = (col & 0x7f7f7f) << 1;
Lighten or darken a hex color
I'm not exactly sure why your code fails, but I do see some potential pitfalls. The most obvious is the percent
parameter. This should be a double
or a single
as the accepted range is >= -1.0
and <= +1.0
.
I've created a simple .net fiddle, available here: https://dotnetfiddle.net/QhowPP
Public Shared Function LightColor(htmlColor As String, percent As Double) As Color
If (String.IsNullOrEmpty(htmlColor)) Then
Throw New ArgumentNullException("htmlColor")
ElseIf ((percent < -1D) Or (percent > +1D)) Then
Throw New ArgumentOutOfRangeException("percent")
End If
Dim c = ColorTranslator.FromHtml(htmlColor)
Dim f = Int32.Parse(htmlColor.Substring(1), Globalization.NumberStyles.AllowHexSpecifier)
Dim t = If((percent < 0), 0, 255)
Dim p = If((percent < 0), (percent * -1), percent)
Dim result = ("#" & CInt(
&H1000000 + (Math.Round((t - c.R) * p) + c.R) *
&H10000 + (Math.Round((t - c.G) * p) + c.G) *
&H100 + (Math.Round((t - c.B) * p) + c.B)
).ToString("X").Substring(1))
Return ColorTranslator.FromHtml(result)
End Function
C#:
public static Color LightColor(String htmlColor, Double percent)
{
if (String.IsNullOrEmpty(htmlColor))
{
throw new ArgumentNullException("htmlColor");
}
else if ((percent < -1D) | (percent > +1D))
{
throw new ArgumentOutOfRangeException("percent");
}
var c = ColorTranslator.FromHtml(htmlColor);
var f = Int32.Parse(htmlColor.Substring(1), System.Globalization.NumberStyles.AllowHexSpecifier);
var t = ((percent < 0) ? 0 : 255);
var p = ((percent < 0) ? (percent * -1) : percent);
var result = ("#" + ((Int32)(
0x1000000 + (Math.Round((t - c.R) * p) + c.R) *
0x10000 + (Math.Round((t - c.G) * p) + c.G) *
0x100 + (Math.Round((t - c.B) * p) + c.B)
)).ToString("X").Substring(1));
return ColorTranslator.FromHtml(result);
}
How could I lighten/darken a hex color in C?
To do it right, first decode the hex values into three integers, r
, g
, and b
, convert to doubles, then convert those doubles into a different 3D colorspace in which lightness or intensity is its own element. Either HSL/HSV or YIQ/YUV (Google "color space"). Then change the intensity, and convert back. That will ensure that you get a darker version of the same hue with the same saturation (more or less--at least it will be much better than simply tweaking the RGB values). Google "RGB to HSL" and such to find code for doing the conversions.
For example:
https://gist.github.com/mjackson/5311256
How can I darken/lighten a RGB color
Something like this maybe:
def interpolate(color_a, color_b, t):
# 'color_a' and 'color_b' are RGB tuples
# 't' is a value between 0.0 and 1.0
# this is a naive interpolation
return tuple(int(a + (b - a) * t) for a, b in zip(color_a, color_b))
def main():
color_a = (175, 250, 255)
color_b = (0, 0, 0)
number_of_steps = 10
colors = [interpolate(color_a, color_b, t/number_of_steps) for t in range(number_of_steps+1)]
for color in colors:
print(color)
return 0
if __name__ == "__main__":
import sys
sys.exit(main())
Output:
(175, 250, 255)
(157, 225, 229)
(140, 200, 204)
(122, 175, 178)
(105, 150, 153)
(87, 125, 127)
(70, 100, 102)
(52, 75, 76)
(35, 50, 51)
(17, 25, 25)
(0, 0, 0)
Programmatically Lighten or Darken a hex color (or rgb, and blend colors)
Well, this answer has become its own beast. Many new versions, it was getting stupid long. Many thanks to all of the great many contributors to this answer. But, in order to keep it simple for the masses. I archived all the versions/history of this answer's evolution to my github. And started it over clean on StackOverflow here with the newest version. A special thanks goes out to Mike 'Pomax' Kamermans for this version. He gave me the new math.
This function (pSBC
) will take a HEX or RGB web color. pSBC
can shade it darker or lighter, or blend it with a second color, and can also pass it right thru but convert from Hex to RGB (Hex2RGB) or RGB to Hex (RGB2Hex). All without you even knowing what color format you are using.
This runs really fast, probably the fastest, especially considering its many features. It was a long time in the making. See the whole story on my github. If you want the absolutely smallest and fastest possible way to shade or blend, see the Micro Functions below and use one of the 2-liner speed demons. They are great for intense animations, but this version here is fast enough for most animations.
This function uses Log Blending or Linear Blending. However, it does NOT convert to HSL to properly lighten or darken a color. Therefore, results from this function will differ from those much larger and much slower functions that use HSL.
jsFiddle with pSBC
github > pSBC Wiki
Features:
- Auto-detects and accepts standard Hex colors in the form of strings. For example:
"#AA6622"
or"#bb551144"
. - Auto-detects and accepts standard RGB colors in the form of strings. For example:
"rgb(123,45,76)"
or"rgba(45,15,74,0.45)"
. - Shades colors to white or black by percentage.
- Blends colors together by percentage.
- Does Hex2RGB and RGB2Hex conversion at the same time, or solo.
- Accepts 3 digit (or 4 digit w/ alpha) HEX color codes, in the form #RGB (or #RGBA). It will expand them. For Example:
"#C41"
becomes"#CC4411"
. - Accepts and (Linear) blends alpha channels. If either the
c0
(from) color or thec1
(to) color has an alpha channel, then the returned color will have an alpha channel. If both colors have an alpha channel, then the returned color will be a linear blend of the two alpha channels using the percentage given (just as if it were a normal color channel). If only one of the two colors has an alpha channel, this alpha will just be passed thru to the returned color. This allows one to blend/shade a transparent color while maintaining the transparency level. Or, if the transparency levels should blend as well, make sure both colors have alphas. When shading, it will pass the alpha channel straight thru. If you want basic shading that also shades the alpha channel, then usergb(0,0,0,1)
orrgb(255,255,255,1)
as yourc1
(to) color (or their hex equivalents). For RGB colors, the returned color's alpha channel will be rounded to 3 decimal places. - RGB2Hex and Hex2RGB conversions are implicit when using blending. Regardless of the
c0
(from) color; the returned color will always be in the color format of thec1
(to) color, if one exists. If there is noc1
(to) color, then pass'c'
in as thec1
color and it will shade and convert whatever thec0
color is. If conversion only is desired, then pass0
in as the percentage (p
) as well. If thec1
color is omitted or a non-string
is passed in, it will not convert. - A secondary function is added to the global as well.
pSBCr
can be passed a Hex or RGB color and it returns an object containing this color information. Its in the form: {r: XXX, g: XXX, b: XXX, a: X.XXX}. Where.r
,.g
, and.b
have range 0 to 255. And when there is no alpha:.a
is -1. Otherwise:.a
has range 0.000 to 1.000. - For RGB output, it outputs
rgba()
overrgb()
when a color with an alpha channel was passed intoc0
(from) and/orc1
(to). - Minor Error Checking has been added. It's not perfect. It can still crash or create jibberish. But it will catch some stuff. Basically, if the structure is wrong in some ways or if the percentage is not a number or out of scope, it will return
null
. An example:pSBC(0.5,"salt") == null
, where as it thinks#salt
is a valid color. Delete the four lines which end withreturn null;
to remove this feature and make it faster and smaller. - Uses Log Blending. Pass
true
in forl
(the 4th parameter) to use Linear Blending.
Code:
// Version 4.0
const pSBC=(p,c0,c1,l)=>{
let r,g,b,P,f,t,h,i=parseInt,m=Math.round,a=typeof(c1)=="string";
if(typeof(p)!="number"||p<-1||p>1||typeof(c0)!="string"||(c0[0]!='r'&&c0[0]!='#')||(c1&&!a))return null;
if(!this.pSBCr)this.pSBCr=(d)=>{
let n=d.length,x={};
if(n>9){
[r,g,b,a]=d=d.split(","),n=d.length;
if(n<3||n>4)return null;
x.r=i(r[3]=="a"?r.slice(5):r.slice(4)),x.g=i(g),x.b=i(b),x.a=a?parseFloat(a):-1
}else{
if(n==8||n==6||n<4)return null;
if(n<6)d="#"+d[1]+d[1]+d[2]+d[2]+d[3]+d[3]+(n>4?d[4]+d[4]:"");
d=i(d.slice(1),16);
if(n==9||n==5)x.r=d>>24&255,x.g=d>>16&255,x.b=d>>8&255,x.a=m((d&255)/0.255)/1000;
else x.r=d>>16,x.g=d>>8&255,x.b=d&255,x.a=-1
}return x};
h=c0.length>9,h=a?c1.length>9?true:c1=="c"?!h:false:h,f=this.pSBCr(c0),P=p<0,t=c1&&c1!="c"?this.pSBCr(c1):P?{r:0,g:0,b:0,a:-1}:{r:255,g:255,b:255,a:-1},p=P?p*-1:p,P=1-p;
if(!f||!t)return null;
if(l)r=m(P*f.r+p*t.r),g=m(P*f.g+p*t.g),b=m(P*f.b+p*t.b);
else r=m((P*f.r**2+p*t.r**2)**0.5),g=m((P*f.g**2+p*t.g**2)**0.5),b=m((P*f.b**2+p*t.b**2)**0.5);
a=f.a,t=t.a,f=a>=0||t>=0,a=f?a<0?t:t<0?a:a*P+t*p:0;
if(h)return"rgb"+(f?"a(":"(")+r+","+g+","+b+(f?","+m(a*1000)/1000:"")+")";
else return"#"+(4294967296+r*16777216+g*65536+b*256+(f?m(a*255):0)).toString(16).slice(1,f?undefined:-2)
}
Usage:
// Setup:
let color1 = "rgb(20,60,200)";
let color2 = "rgba(20,60,200,0.67423)";
let color3 = "#67DAF0";
let color4 = "#5567DAF0";
let color5 = "#F3A";
let color6 = "#F3A9";
let color7 = "rgb(200,60,20)";
let color8 = "rgba(200,60,20,0.98631)";
// Tests:
/*** Log Blending ***/
// Shade (Lighten or Darken)
pSBC ( 0.42, color1 ); // rgb(20,60,200) + [42% Lighter] => rgb(166,171,225)
pSBC ( -0.4, color5 ); // #F3A + [40% Darker] => #c62884
pSBC ( 0.42, color8 ); // rgba(200,60,20,0.98631) + [42% Lighter] => rgba(225,171,166,0.98631)
// Shade with Conversion (use "c" as your "to" color)
pSBC ( 0.42, color2, "c" ); // rgba(20,60,200,0.67423) + [42% Lighter] + [Convert] => #a6abe1ac
// RGB2Hex & Hex2RGB Conversion Only (set percentage to zero)
pSBC ( 0, color6, "c" ); // #F3A9 + [Convert] => rgba(255,51,170,0.6)
// Blending
pSBC ( -0.5, color2, color8 ); // rgba(20,60,200,0.67423) + rgba(200,60,20,0.98631) + [50% Blend] => rgba(142,60,142,0.83)
pSBC ( 0.7, color2, color7 ); // rgba(20,60,200,0.67423) + rgb(200,60,20) + [70% Blend] => rgba(168,60,111,0.67423)
pSBC ( 0.25, color3, color7 ); // #67DAF0 + rgb(200,60,20) + [25% Blend] => rgb(134,191,208)
pSBC ( 0.75, color7, color3 ); // rgb(200,60,20) + #67DAF0 + [75% Blend] => #86bfd0
/*** Linear Blending ***/
// Shade (Lighten or Darken)
pSBC ( 0.42, color1, false, true ); // rgb(20,60,200) + [42% Lighter] => rgb(119,142,223)
pSBC ( -0.4, color5, false, true ); // #F3A + [40% Darker] => #991f66
pSBC ( 0.42, color8, false, true ); // rgba(200,60,20,0.98631) + [42% Lighter] => rgba(223,142,119,0.98631)
// Shade with Conversion (use "c" as your "to" color)
pSBC ( 0.42, color2, "c", true ); // rgba(20,60,200,0.67423) + [42% Lighter] + [Convert] => #778edfac
// RGB2Hex & Hex2RGB Conversion Only (set percentage to zero)
pSBC ( 0, color6, "c", true ); // #F3A9 + [Convert] => rgba(255,51,170,0.6)
// Blending
pSBC ( -0.5, color2, color8, true ); // rgba(20,60,200,0.67423) + rgba(200,60,20,0.98631) + [50% Blend] => rgba(110,60,110,0.83)
pSBC ( 0.7, color2, color7, true ); // rgba(20,60,200,0.67423) + rgb(200,60,20) + [70% Blend] => rgba(146,60,74,0.67423)
pSBC ( 0.25, color3, color7, true ); // #67DAF0 + rgb(200,60,20) + [25% Blend] => rgb(127,179,185)
pSBC ( 0.75, color7, color3, true ); // rgb(200,60,20) + #67DAF0 + [75% Blend] => #7fb3b9
/*** Other Stuff ***/
// Error Checking
pSBC ( 0.42, "#FFBAA" ); // #FFBAA + [42% Lighter] => null (Invalid Input Color)
pSBC ( 42, color1, color5 ); // rgb(20,60,200) + #F3A + [4200% Blend] => null (Invalid Percentage Range)
pSBC ( 0.42, {} ); // [object Object] + [42% Lighter] => null (Strings Only for Color)
pSBC ( "42", color1 ); // rgb(20,60,200) + ["42"] => null (Numbers Only for Percentage)
pSBC ( 0.42, "salt" ); // salt + [42% Lighter] => null (A Little Salt is No Good...)
// Error Check Fails (Some Errors are not Caught)
pSBC ( 0.42, "#salt" ); // #salt + [42% Lighter] => #a5a5a500 (...and a Pound of Salt is Jibberish)
// Ripping
pSBCr ( color4 ); // #5567DAF0 + [Rip] => [object Object] => {'r':85,'g':103,'b':218,'a':0.941}
The picture below will help show the difference in the two blending methods:
Micro Functions
If you really want speed and size, you will have to use RGB not HEX. RGB is more straightforward and simple, HEX writes too slow and comes in too many flavors for a simple two-liner (IE. it could be a 3, 4, 6, or 8 digit HEX code). You will also need to sacrifice some features, no error checking, no HEX2RGB nor RGB2HEX. As well, you will need to choose a specific function (based on its function name below) for the color blending math, and if you want shading or blending. These functions do support alpha channels. And when both input colors have alphas it will Linear Blend them. If only one of the two colors has an alpha, it will pass it straight thru to the resulting color. Below are two liner functions that are incredibly fast and small:
const RGB_Linear_Blend=(p,c0,c1)=>{
var i=parseInt,r=Math.round,P=1-p,[a,b,c,d]=c0.split(","),[e,f,g,h]=c1.split(","),x=d||h,j=x?","+(!d?h:!h?d:r((parseFloat(d)*P+parseFloat(h)*p)*1000)/1000+")"):")";
return"rgb"+(x?"a(":"(")+r(i(a[3]=="a"?a.slice(5):a.slice(4))*P+i(e[3]=="a"?e.slice(5):e.slice(4))*p)+","+r(i(b)*P+i(f)*p)+","+r(i(c)*P+i(g)*p)+j;
}
const RGB_Linear_Shade=(p,c)=>{
var i=parseInt,r=Math.round,[a,b,c,d]=c.split(","),P=p<0,t=P?0:255*p,P=P?1+p:1-p;
Related Topics
Angularjs Ui-Router Login Authentication
Using Rails 3.1, Where Do You Put Your "Page Specific" JavaScript Code
How to Detect Pressing Enter on the Keyboard Using Jquery
Javascript, Node.Js: Is Array.Foreach Asynchronous
Cross Browser JavaScript (Not Jquery...) Scroll to Top Animation
The Value of "This" Within the Handler Using Addeventlistener
How to Use Meteor Methods Inside of a Template Helper
How to Turn This Callback into a Promise Using Async/Await
Is ".Then(Function(A){ Return A; })" a No-Op for Promises
How to Check a Not-Defined Variable in JavaScript
Operator Precedence with JavaScript Ternary Operator
Why Is Mutating the [[Prototype]] of an Object Bad for Performance
Code Within D3.JSON() Callback Is Not Executed
Nested Routes with React Router V4/V5
Detecting When User Scrolls to Bottom of Div with Jquery
Object.Watch() for All Browsers