程序化地变亮或变暗十六进制颜色(或rgb,和混合颜色)

这是一个我正在工作的函数,以编程方式使十六进制颜色变亮或变暗。只需要传入一个字符串,比如"3F6D2A"表示颜色(col),一个base10整数(amt)表示要变亮或变暗的量。为了变暗,传递一个负数(即-20)。

我这么做的原因是,到目前为止,我找到的所有解决方案似乎都把问题复杂化了。我有一种感觉,只需几行代码就可以完成。请让我知道,如果你发现任何问题,或有任何调整,以加快它。

function LightenDarkenColor(col, amt) {
col = parseInt(col, 16);
return (((col & 0x0000FF) + amt) | ((((col >> 8) & 0x00FF) + amt) << 8) | (((col >> 16) + amt) << 16)).toString(16);
}




// TEST
console.log( LightenDarkenColor("3F6D2A",40) );

对于开发人员来说,这里有一个更容易阅读的版本:

function LightenDarkenColor(col, amt) {
var num = parseInt(col, 16);
var r = (num >> 16) + amt;
var b = ((num >> 8) & 0x00FF) + amt;
var g = (num & 0x0000FF) + amt;
var newColor = g | (b << 8) | (r << 16);
return newColor.toString(16);
}




// TEST
console.log(LightenDarkenColor("3F6D2A", -40));

最后一个版本来处理可能(或可能没有)有"#"一开始。加上调整不适当的颜色值:

function LightenDarkenColor(col,amt) {
var usePound = false;
if ( col[0] == "#" ) {
col = col.slice(1);
usePound = true;
}


var num = parseInt(col,16);


var r = (num >> 16) + amt;


if ( r > 255 ) r = 255;
else if  (r < 0) r = 0;


var b = ((num >> 8) & 0x00FF) + amt;


if ( b > 255 ) b = 255;
else if  (b < 0) b = 0;
    

var g = (num & 0x0000FF) + amt;


if ( g > 255 ) g = 255;
else if  ( g < 0 ) g = 0;


return (usePound?"#":"") + (g | (b << 8) | (r << 16)).toString(16);
}

好吧,现在它不只是几行,但它似乎简单得多,如果你不使用"#"并且不需要检查超出范围的颜色,它只是几行。

如果不使用"#",你可以将它添加到如下代码中:

var myColor = "3F6D2A";
myColor = LightenDarkenColor(myColor,10);
thePlaceTheColorIsUsed = ("#" + myColor);

我想我的主要问题是,我说的对吗?这不是包括大多数(正常)情况吗?如果是的话,最快最小的方法是什么?我想在动画和小环境中使用,所以速度是这里的第一个最重要的因素,尺寸第二,准确性第三,可读性?嗯?不在要求清单上(对不起,我知道你们中有一半人正在泪眼汪汪!)

295855 次浏览

你考虑过RGB > HSL转换吗?然后上下移动亮度?这就是我要走的路。

快速查找一些算法得到了以下网站。

< p > PHP: # EYZ0 < / p > < p > <罢工> Javascript: # EYZ0罢工< / > < / p >

编辑上面的链接不再有效。你可以查看页面源代码要点的git中心

或者,另一个StackOverflow 问题可能是一个好地方。


尽管这不是OP的正确选择,但以下是我最初建议的代码的近似值。(假设你有rgb/hsl转换函数)

var SHADE_SHIFT_AMOUNT = 0.1;


function lightenShade(colorValue)
{
if(colorValue && colorValue.length >= 6)
{
var redValue = parseInt(colorValue.slice(-6,-4), 16);
var greenValue = parseInt(colorValue.slice(-4,-2), 16);
var blueValue = parseInt(colorValue.slice(-2), 16);


var hsl = rgbToHsl(redValue, greenValue, blueValue);
hsl[2]= Math.min(hsl[2] + SHADE_SHIFT_AMOUNT, 1);
var rgb = hslToRgb(hsl[0], hsl[1], hsl[2]);
return "#" + rgb[0].toString(16) + rgb[1].toString(16) + rgb[2].toString(16);
}
return null;
}


function darkenShade(colorValue)
{
if(colorValue && colorValue.length >= 6)
{
var redValue = parseInt(colorValue.slice(-6,-4), 16);
var greenValue = parseInt(colorValue.slice(-4,-2), 16);
var blueValue = parseInt(colorValue.slice(-2), 16);


var hsl = rgbToHsl(redValue, greenValue, blueValue);
hsl[2]= Math.max(hsl[2] - SHADE_SHIFT_AMOUNT, 0);
var rgb = hslToRgb(hsl[0], hsl[1], hsl[2]);
return "#" + rgb[0].toString(16) + rgb[1].toString(16) + rgb[2].toString(16);
}
return null;
}

这样的假设:

  1. 函数hslToRgbrgbToHsl
  2. 参数colorValue是一个形式为# RRGGBB的字符串

虽然我们讨论的是css,但在IE9/Chrome/Firefox中,有一种语法可以指定奥软/ hsla

我尝试了你的函数,有一个小错误:如果一些最终的'r'值只有1位,结果就像:'a0a0a',而正确的值是'0a0a0a',例如。 我只是通过添加这个而不是你的返回来快速修复它:

var rStr = (r.toString(16).length < 2)?'0'+r.toString(16):r.toString(16);
var gStr = (g.toString(16).length < 2)?'0'+g.toString(16):g.toString(16);
var bStr = (b.toString(16).length < 2)?'0'+b.toString(16):b.toString(16);


return (usePound?"#":"") + rStr + gStr + bStr;

也许不是很好,但确实有用。顺便说一句,功能很好。这正是我需要的。:)

我做了一个很适合我的解决方案:

function shadeColor(color, percent) {


var R = parseInt(color.substring(1,3),16);
var G = parseInt(color.substring(3,5),16);
var B = parseInt(color.substring(5,7),16);


R = parseInt(R * (100 + percent) / 100);
G = parseInt(G * (100 + percent) / 100);
B = parseInt(B * (100 + percent) / 100);


R = (R<255)?R:255;
G = (G<255)?G:255;
B = (B<255)?B:255;


var RR = ((R.toString(16).length==1)?"0"+R.toString(16):R.toString(16));
var GG = ((G.toString(16).length==1)?"0"+G.toString(16):G.toString(16));
var BB = ((B.toString(16).length==1)?"0"+B.toString(16):B.toString(16));


return "#"+RR+GG+BB;
}

示例减轻:

shadeColor("#63C6FF",40);

示例变暗:

shadeColor("#63C6FF",-40);

好吧,这个答案已经自成一体了。许多新版本,它变得愚蠢的长。非常感谢所有对这个答案做出贡献的人。但是,为了让它对大众来说简单。我将这个答案的进化的所有版本/历史存档到我的github。并用最新版本在StackOverflow上重新开始。特别感谢< A href="https://stackoverflow.com/users/740553/mike-pomax-kamermans">Mike 'Pomax' Kamermans为这个版本。他给了我新的数学。


这个函数(pSBC)将采用十六进制或RGB的网页颜色。pSBC可以使它变深或变浅,或与第二种颜色混合,也可以直接通过,但可以从十六进制转换为RGB (Hex2RGB)或RGB转换为十六进制(RGB2Hex)。你甚至不知道你使用的是什么颜色格式。

它运行得非常快,可能是最快的,特别是考虑到它的许多特性。这是一个漫长的过程。在我的github上看到整个故事。如果你想要绝对最小和最快的方法来着色或混合,请参阅下面的微函数,并使用其中一个2线速度恶魔。他们是伟大的激烈的动画,但这个版本在这里是足够快的大多数动画。

这个函数使用对数混合或线性混合。但是,它不能转换为HSL来适当地变亮或变暗颜色。因此,这个函数的结果会有所不同来自那些使用HSL的更大更慢的函数。

jsFiddle with pSBC

github >pSBC Wiki < / >

特点:

  • 自动检测和接受字符串形式的标准十六进制颜色。例如:"#AA6622""#bb551144"
  • 自动检测和接受字符串形式的标准RGB颜色。例如:"rgb(123,45,76)""rgba(45,15,74,0.45)"
  • 阴影颜色为白色或黑色的百分比。
  • 按百分比混合颜色。
  • 同时进行Hex2RGB和RGB2Hex转换,或单独进行。
  • 接受3位(或4位w/ alpha) HEX颜色代码,形式为#RGB(或#RGBA)。它会扩大它们。例如:"#C41"变成"#CC4411"
  • 接受和(线性)混合alpha通道。如果c0 (from)颜色或c1 (to)颜色有alpha通道,则返回的颜色将有alpha通道。如果两种颜色都有alpha通道,那么返回的颜色将是使用给定百分比的两个alpha通道的线性混合(就像它是一个正常的颜色通道一样)。如果两种颜色中只有一种有alpha通道,这个alpha通道将被传递给返回的颜色。这允许一个混合/阴影的透明颜色,同时保持透明度水平。或者,如果透明度级别也应该混合,请确保两种颜色都有alpha。当着色时,它将直接通过alpha通道。如果你想要基本的阴影也阴影alpha通道,然后使用rgb(0,0,0,1)rgb(255,255,255,1)作为你的c1 (to)颜色(或他们的十六进制等效物)。对于RGB颜色,返回颜色的alpha通道将四舍五入到小数点后3位。
  • RGB2Hex和Hex2RGB转换在使用混合时是隐式的。不管c0 (from)颜色;返回的颜色格式总是c1 (to)颜色,如果有的话。如果没有c1(到)颜色,然后传递'c'作为c1颜色,它将着色和转换任何c0颜色。如果只需要转换,则将0作为百分比(p)传入。如果省略了c1颜色或传入了非string颜色,它将不会转换。
  • 一个辅助函数也被添加到全局函数中。pSBCr可以传递一个十六进制或RGB颜色,它返回一个包含此颜色信息的对象。它的形式是:{r: XXX, g: XXX, b: XXX, a: x.x XXX}。其中.r.g.b的范围为0到255。当没有alpha时,.a是-1。否则:.a的范围为0.000到1.000。
  • 对于RGB输出,当一个带有alpha通道的颜色被传递到c0 (from)和/或c1 (to)时,它输出rgba() / rgb()
  • 新增“轻微错误检查”。它并不完美。它仍然可能崩溃或产生胡言乱语。但它会捕获一些东西。基本上,如果结构在某些方面错误,或者百分比不是数字或超出范围,它将返回null。一个例子:pSBC(0.5,"salt") == null,它认为#salt是一个有效的颜色。删除以return null;结尾的四行以删除该特性,并使其更快更小。
  • 使用日志混合。将true传递给l(第四个参数)来使用线性混合。

代码:

// 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)
}

用法:

// 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}

下面的图片将有助于显示两种混合方法的区别:

< img src = " https://i.imgur.com/FBe90R8.png " alt = " / >


微型功能

如果你真的想要速度和大小,你必须使用RGB而不是HEX。RGB更直接和简单,HEX写得太慢,对于一个简单的两行程序(IE。它可以是3、4、6或8位HEX代码)。你还需要牺牲一些特性,没有错误检查,没有HEX2RGB或RGB2HEX。同样,如果你想要着色或混合,你将需要选择一个特定的函数(基于下面的函数名)进行颜色混合。这些函数确实支持alpha通道。当两个输入颜色都有alpha时,它将线性混合它们。如果两种颜色中只有一种有alpha,它将直接将其传递到结果颜色。下面是两个非常快且非常小的线性函数:

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:")");
}

想了解更多信息?阅读关于github的完整报道。

PT

(注:如果有人有其他混合方法的数学,请分享。)

< p > c#版本…… 注意,我得到的颜色字符串的格式是#FF12AE34,需要剪掉#FF.

    private string GetSmartShadeColorByBase(string s, float percent)
{
if (string.IsNullOrEmpty(s))
return "";
var r = s.Substring(3, 2);
int rInt = int.Parse(r, NumberStyles.HexNumber);
var g = s.Substring(5, 2);
int gInt = int.Parse(g, NumberStyles.HexNumber);
var b = s.Substring(7, 2);
int bInt = int.Parse(b, NumberStyles.HexNumber);


var t = percent < 0 ? 0 : 255;
var p = percent < 0 ? percent*-1 : percent;


int newR = Convert.ToInt32(Math.Round((t - rInt) * p) + rInt);
var newG = Convert.ToInt32(Math.Round((t - gInt) * p) + gInt);
var newB = Convert.ToInt32(Math.Round((t - bInt) * p) + bInt);


return String.Format("#{0:X2}{1:X2}{2:X2}", newR, newG, newB);
}

如何简单的阴影颜色在PHP?

<?php
function shadeColor ($color='#cccccc', $percent=-25) {


$color = Str_Replace("#",Null,$color);


$r = Hexdec(Substr($color,0,2));
$g = Hexdec(Substr($color,2,2));
$b = Hexdec(Substr($color,4,2));


$r = (Int)($r*(100+$percent)/100);
$g = (Int)($g*(100+$percent)/100);
$b = (Int)($b*(100+$percent)/100);


$r = Trim(Dechex(($r<255)?$r:255));
$g = Trim(Dechex(($g<255)?$g:255));
$b = Trim(Dechex(($b<255)?$b:255));


$r = ((Strlen($r)==1)?"0{$r}":$r);
$g = ((Strlen($g)==1)?"0{$g}":$g);
$b = ((Strlen($b)==1)?"0{$b}":$b);


return (String)("#{$r}{$g}{$b}");
}


echo shadeColor(); // #999999

这是基于函数的。我更喜欢使用步骤而不是百分比,因为这对我来说更直观。

例如,蓝色值200的20%与蓝色值40的20%有很大不同。

不管怎样,这是我的修改,谢谢你原来的功能。

function adjustBrightness(col, amt) {


var usePound = false;


if (col[0] == "#") {
col = col.slice(1);
usePound = true;
}


var R = parseInt(col.substring(0,2),16);
var G = parseInt(col.substring(2,4),16);
var B = parseInt(col.substring(4,6),16);


// to make the colour less bright than the input
// change the following three "+" symbols to "-"
R = R + amt;
G = G + amt;
B = B + amt;


if (R > 255) R = 255;
else if (R < 0) R = 0;


if (G > 255) G = 255;
else if (G < 0) G = 0;


if (B > 255) B = 255;
else if (B < 0) B = 0;


var RR = ((R.toString(16).length==1)?"0"+R.toString(16):R.toString(16));
var GG = ((G.toString(16).length==1)?"0"+G.toString(16):G.toString(16));
var BB = ((B.toString(16).length==1)?"0"+B.toString(16):B.toString(16));


return (usePound?"#":"") + RR + GG + BB;


}

我做了一个优秀的xcolor库的移植,以消除它的jQuery依赖。里面有很多功能,包括调亮和调暗颜色。

实际上,将十六进制转换为RGB是一个完全独立于颜色变亮或变暗的功能。请保持衣物干燥。在任何情况下,一旦你有了RGB颜色,你可以把你想要的光级别和你必须的光级别之间的差值加到每个RGB值上:

var lightness = function(level) {
if(level === undefined) {
return Math.max(this.g,this.r,this.b)
} else {
var roundedLevel = Math.round(level) // fractions won't work here
var levelChange = roundedLevel - this.lightness()


var r = Math.max(0,this.r+levelChange)
var g = Math.max(0,this.g+levelChange)
var b = Math.max(0,this.b+levelChange)


if(r > 0xff) r = 0xff
if(g > 0xff) g = 0xff
if(b > 0xff) b = 0xff


return xolor({r: r, g: g, b: b})
}
}


var lighter = function(amount) {
return this.lightness(this.lightness()+amount)
}

参见https://github.com/fresheneesz/xolor获取更多源代码。

下面的方法将允许您使十六进制(Hex)颜色字符串的曝光值变亮或变暗:

private static string GetHexFromRGB(byte r, byte g, byte b, double exposure)
{
exposure = Math.Max(Math.Min(exposure, 1.0), -1.0);
if (exposure >= 0)
{
return "#"
+ ((byte)(r + ((byte.MaxValue - r) * exposure))).ToString("X2")
+ ((byte)(g + ((byte.MaxValue - g) * exposure))).ToString("X2")
+ ((byte)(b + ((byte.MaxValue - b) * exposure))).ToString("X2");
}
else
{
return "#"
+ ((byte)(r + (r * exposure))).ToString("X2")
+ ((byte)(g + (g * exposure))).ToString("X2")
+ ((byte)(b + (b * exposure))).ToString("X2");
}


}

对于GetHexFromRGB()中的最后一个参数值,传递一个介于-1和1之间的双值(-1为黑色,0不变,1为白色):

// split color (#e04006) into three strings
var r = Convert.ToByte("e0", 16);
var g = Convert.ToByte("40", 16);
var b = Convert.ToByte("06", 16);


GetHexFromRGB(r, g, b, 0.25);  // Lighten by 25%;

我一直希望能够产生色彩的色调/深浅,这是我的JavaScript解决方案:

const varyHue = function (hueIn, pcIn) {
const truncate = function (valIn) {
if (valIn > 255) {
valIn = 255;
} else if (valIn < 0)  {
valIn = 0;
}
return valIn;
};


let red   = parseInt(hueIn.substring(0, 2), 16);
let green = parseInt(hueIn.substring(2, 4), 16);
let blue  = parseInt(hueIn.substring(4, 6), 16);
let pc    = parseInt(pcIn, 10);    //shade positive, tint negative
let max   = 0;
let dif   = 0;


max = red;


if (pc < 0) {    //tint: make lighter
if (green < max) {
max = green;
}


if (blue < max) {
max = blue;
}


dif = parseInt(((Math.abs(pc) / 100) * (255 - max)), 10);


return leftPad(((truncate(red + dif)).toString(16)), '0', 2)  + leftPad(((truncate(green + dif)).toString(16)), '0', 2) + leftPad(((truncate(blue + dif)).toString(16)), '0', 2);
} else {    //shade: make darker
if (green > max) {
max = green;
}


if (blue > max) {
max = blue;
}


dif = parseInt(((pc / 100) * max), 10);


return leftPad(((truncate(red - dif)).toString(16)), '0', 2)  + leftPad(((truncate(green - dif)).toString(16)), '0', 2) + leftPad(((truncate(blue - dif)).toString(16)), '0', 2);
}
};

我想改变一个颜色的具体的亮度级别-无论颜色之前是什么亮度-这里有一个简单的JS函数,似乎工作得很好,尽管我相信它可以更短

function setLightPercentage(col: any, p: number) {
const R = parseInt(col.substring(1, 3), 16);
const G = parseInt(col.substring(3, 5), 16);
const B = parseInt(col.substring(5, 7), 16);
const curr_total_dark = (255 * 3) - (R + G + B);


// calculate how much of the current darkness comes from the different channels
const RR = ((255 - R) / curr_total_dark);
const GR = ((255 - G) / curr_total_dark);
const BR = ((255 - B) / curr_total_dark);


// calculate how much darkness there should be in the new color
const new_total_dark = ((255 - 255 * (p / 100)) * 3);


// make the new channels contain the same % of available dark as the old ones did
const NR = 255 - Math.round(RR * new_total_dark);
const NG = 255 - Math.round(GR * new_total_dark);
const NB = 255 - Math.round(BR * new_total_dark);


const RO = ((NR.toString(16).length === 1) ? "0" + NR.toString(16) : NR.toString(16));
const GO = ((NG.toString(16).length === 1) ? "0" + NG.toString(16) : NG.toString(16));
const BO = ((NB.toString(16).length === 1) ? "0" + NB.toString(16) : NB.toString(16));


return "#" + RO + GO + BO;}

下面是一个基于Eric的回答的超级简单的一行代码

function adjust(color, amount) {
return '#' + color.replace(/^#/, '').replace(/../g, color => ('0'+Math.min(255, Math.max(0, parseInt(color, 16) + amount)).toString(16)).substr(-2));
}

例子:

adjust('#ffffff', -20) => "#ebebeb"
adjust('000000', 20) => "#141414"

你的方法是可以的:)我简化了你的最短版本一点点(饱和度控制看在这里)

(col,amt)=> (+('0x'+col)+amt*0x010101).toString(16).padStart(6,0)

// Similar to OP shortest version, we not have here # and colors range checking


var LightenDarkenColor =
(col,amt) => (+('0x'+col)+amt*0x010101).toString(16).padStart(6,0);








// ------
// TEST
// ------


function update() {
let c= col.value.padEnd(6,'0').slice(0,6);
let color = '#'+LightenDarkenColor(c, +amt.value);
oldColor.innerHTML = 'Old: #'+c;
oldColor.style = `background: #${c}`;
newColor.innerHTML = 'New: '+color
newColor.style = `background: ${color}`;


  

}


update();
.box{ width: 100px; height: 100px; margin: 10px; display: inline-block}
<input id="col" value="3F6D2A" oninput="update()">
<input id="amt" value="30" oninput="update()"><br>
<div id="oldColor" class="box"></div>
<div id="newColor" class="box"></div>

和版本#和颜色范围检查

// # and colors range checking


var LightenDarkenColor =
(col,amt) => '#'+col.slice(1).match(/../g)
.map(x=>(x=+`0x${x}`+amt,x<0?0:(x>255?255:x))
.toString(16).padStart(2,0)).join``;








// ------
// TEST
// ------


function update() {
let c= col.value.padEnd(6,'0').slice(0,7);
let color = LightenDarkenColor(c, +amt.value);
oldColor.innerHTML = 'Old: '+c;
oldColor.style = `background: ${c}`;
newColor.innerHTML = 'New: '+color
newColor.style = `background: ${color}`;
}


update();
.box{ width: 100px; height: 100px; margin: 10px; display: inline-block}
<input id="col" value="#3F6D2A" oninput="update()">
<input id="amt" value="40" oninput="update()"><br>
<div id="oldColor" class="box"></div>
<div id="newColor" class="box"></div>

我在c#中需要它,它可以帮助。net开发人员

public static string LightenDarkenColor(string color, int amount)
{
int colorHex = int.Parse(color, System.Globalization.NumberStyles.HexNumber);
string output = (((colorHex & 0x0000FF) + amount) | ((((colorHex >> 0x8) & 0x00FF) + amount) << 0x8) | (((colorHex >> 0xF) + amount) << 0xF)).ToString("x6");
return output;
}

我在这里加上我的2美分,一个令人满意的不同答案的小组合:

const colorShade = (col, amt) => {
col = col.replace(/^#/, '')
if (col.length === 3) col = col[0] + col[0] + col[1] + col[1] + col[2] + col[2]


let [r, g, b] = col.match(/.{2}/g);
([r, g, b] = [parseInt(r, 16) + amt, parseInt(g, 16) + amt, parseInt(b, 16) + amt])


r = Math.max(Math.min(255, r), 0).toString(16)
g = Math.max(Math.min(255, g), 0).toString(16)
b = Math.max(Math.min(255, b), 0).toString(16)


const rr = (r.length < 2 ? '0' : '') + r
const gg = (g.length < 2 ? '0' : '') + g
const bb = (b.length < 2 ? '0' : '') + b


return `#${rr}${gg}${bb}`
}


接受以#开头的颜色,6个字符或3个字符。

使用示例:colorShade('#54b946', -40)

这里是4种颜色的输出,每个颜色都有3个浅度和3个深度(这里的数量是40的倍数)。

enter image description here

我只是使用了“#”前面的十六进制数字。

var x = 0xf0f0f0;
x=x+0xf00; //set this value as you wish programatically
document.getElementById("heading").style = 'background-color: #'+x.toString(16);


更高的数字..颜色变浅

不支持从00开始的颜色,即“#000623”;但解决办法是这样的

function lightenDarkenColor(colorCode, amount) {
let usePound = false;


if (colorCode[0] == "#") {
colorCode = colorCode.slice(1);
usePound = true;
}
const num = parseInt(colorCode, 16);
let r = (num >> 16) + amount;


if (r > 255) {
r = 255;
} else if (r < 0) {
r = 0;
}


let b = ((num >> 8) & 0x00FF) + amount;


if (b > 255) {
b = 255;
} else if (b < 0) {
b = 0;
}


let g = (num & 0x0000FF) + amount;


if (g > 255) {
g = 255;
} else if (g < 0) {
g = 0;
}
let color = (g | (b << 8) | (r << 16)).toString(16);
while (color.length < 6){
color = 0 + color;
}
return (usePound ? '#' : '') + color;
}

我的版本写在typescript:

function changeColorLightness(color: number, lightness: number): number {
return (Math.max(0, Math.min(((color & 0xFF0000) / 0x10000) + lightness, 0xFF)) * 0x10000) +
(Math.max(0, Math.min(((color & 0x00FF00) / 0x100) + lightness, 0xFF)) * 0x100) +
(Math.max(0, Math.min(((color & 0x0000FF)) + lightness, 0xFF)));
}

解释:

export function changeColorLightness(color: number, lightness: number): number {
const r = (color & 0xFF0000) / 0x10**4;
const g = (color & 0x00FF00) / 0x10**2;
const b = (color & 0x0000FF);


const changedR = Math.max(0, Math.min(r + lightness, 0xFF));
const changedG = Math.max(0, Math.min(g + lightness, 0xFF));
const changedB = Math.max(0, Math.min(b + lightness, 0xFF));


return (changedR * 0x10**4) + (changedG * 0x10**2) + changedB;
}

用法:

changeColorLightness(0x00FF00, 0x50);
changeColorLightness(parseInt("#00FF00".replace('#',''), 16), 0x50);
changeColorLightness(0x00FF00, 127.5);

在David Sherret和Pablo的基础上,上面的答案将解决方案转换为更安全的Typescript版本

/**
* @param color Hex value format: #ffffff or ffffff
* @param decimal lighten or darken decimal value, example 0.5 to lighten by 50% or 1.5 to darken by 50%.
*/
static shadeColor(color: string, decimal: number): string {
const base = color.startsWith('#') ? 1 : 0;


let r = parseInt(color.substring(base, 3), 16);
let g = parseInt(color.substring(base + 2, 5), 16);
let b = parseInt(color.substring(base + 4, 7), 16);


r = Math.round(r / decimal);
g = Math.round(g / decimal);
b = Math.round(b / decimal);


r = (r < 255)? r : 255;
g = (g < 255)? g : 255;
b = (b < 255)? b : 255;


const rr = ((r.toString(16).length === 1)? `0${r.toString(16)}` : r.toString(16));
const gg = ((g.toString(16).length === 1)? `0${g.toString(16)}` : g.toString(16));
const bb = ((b.toString(16).length === 1)? `0${b.toString(16)}` : b.toString(16));


return `#${rr}${gg}${bb}`;
}
  

我在TypeScript中把这个答案https://stackoverflow.com/a/13542669/4537906重写成一对可读的函数。

原因是在现代JavaScript中,我们不再需要关心保存字符。这是由编译器完成的。在我看来,我们的目标应该是编写可读性和可理解的代码。

这是我的方法:

tint.ts

type ColorObject = Record<"r" | "g" | "b" | "a", number>;
const singleColorSpace = 16 * 16; // 256
const blueSpace = singleColorSpace;
const greenSpace = blueSpace * singleColorSpace; // 65536
const redSpace = greenSpace * singleColorSpace; // 16777216
/* eslint-disable regex/invalid */
// adapted to TS from https://github.com/PimpTrizkit/PJs/wiki/12.-Shade,-Blend-and-Convert-a-Web-Color-(pSBC.js)
export const toColorObject = (rgbOrHex: string): ColorObject => {
const { length } = rgbOrHex;
const outputColor = {} as ColorObject;
if (length > 9) {
const rgbaColor = rgbOrHex.split(",");
const [rgbaAndRed, green, blue, alpha] = rgbaColor;


if (rgbaAndRed.slice(0, 3) !== "rgb") {
throw new Error("Invalid color format");
}
const red = rgbaAndRed[3] === "a" ? rgbaAndRed.slice(5) : rgbaAndRed.slice(4);


const rgbaLength = rgbaColor.length;
if (rgbaLength < 3 || rgbaLength > 4) {
return null;
}
outputColor.r = parseInt(red, 10);
outputColor.g = parseInt(green, 10);
outputColor.b = parseInt(blue, 10);
outputColor.a = alpha ? parseFloat(alpha) : -1;
} else {
if (length === 8 || length === 6 || length < 4) {
throw new Error("Invalid hex color format");
}
let HexColor = rgbOrHex;
if (length < 6) {
HexColor = `#${rgbOrHex[1]}${rgbOrHex[1]}${rgbOrHex[2]}${rgbOrHex[2]}${rgbOrHex[3]}${rgbOrHex[3]}${
length > 4 ? rgbOrHex[4] + rgbOrHex[4] : ""
}`;
}
if (length === 9 || length === 5) {
const hexRed = parseInt(HexColor.slice(1, 3), 16);
outputColor.r = hexRed;


const hexGreen = parseInt(HexColor.slice(3, 5), 16);
outputColor.g = hexGreen;


const hexBlue = parseInt(HexColor.slice(5, 7), 16);
outputColor.b = hexBlue;


const hexAlpha = parseInt(HexColor.slice(7, 9), 16);
outputColor.a = Math.round((hexAlpha / 255) * 100) / 100;
} else {
const hexRed = parseInt(HexColor.slice(1, 3), 16);
outputColor.r = hexRed;


const hexGreen = parseInt(HexColor.slice(3, 5), 16);
outputColor.g = hexGreen;


const hexBlue = parseInt(HexColor.slice(5, 7), 16);
outputColor.b = hexBlue;


outputColor.a = -1;
}
}
return outputColor;
};


const black: ColorObject = { r: 0, g: 0, b: 0, a: -1 };
const white: ColorObject = { r: 255, g: 255, b: 255, a: -1 };
export const tint = (
ratio: number,
inputColor: string,
{ toColor, useLinear, reformat }: { toColor?: string; useLinear?: boolean; reformat?: boolean } = {}
) => {
const { round } = Math;
const clampedRatio = Math.min(Math.max(ratio, -1), 1);
if (ratio < -1 || ratio > 1) {
// eslint-disable-next-line no-console
console.info(`Ratio should be between -1 and 1 and it is ${ratio}. It will be clamped to ${clampedRatio}`);
}
let baseColor = inputColor;
if (inputColor[0] !== "r" && inputColor[0] !== "#") {
baseColor = "#000";
// eslint-disable-next-line no-console
console.info(
`Invalid input color format. "${inputColor}" should be rgb(a) or hex. It will fallback to "${baseColor}"`
);
}
let isRGBformat = baseColor.length > 9 || baseColor.includes("rgb(");
isRGBformat = reformat ? !isRGBformat : isRGBformat;


if (toColor) {
const isToColorRgbFormat = (toColor && toColor?.length > 9) || toColor?.includes("rgb(");
isRGBformat = reformat ? !isToColorRgbFormat : isToColorRgbFormat;
}
const formattedBaseColor = toColorObject(baseColor);
const isNegativeRatio = clampedRatio < 0;
const toColorDefault = isNegativeRatio ? black : white;
const formattedToColor = toColor && !reformat ? toColorObject(toColor) : toColorDefault;
const toColorRatio = Math.abs(clampedRatio);
const baseRatio = 1 - toColorRatio;


const outputColor = {} as ColorObject;
if (useLinear) {
outputColor.r = round(baseRatio * formattedBaseColor.r + toColorRatio * formattedToColor.r);
outputColor.g = round(baseRatio * formattedBaseColor.g + toColorRatio * formattedToColor.g);
outputColor.b = round(baseRatio * formattedBaseColor.b + toColorRatio * formattedToColor.b);
} else {
outputColor.r = round((baseRatio * formattedBaseColor.r ** 2 + toColorRatio * formattedToColor.r ** 2) ** 0.5);
outputColor.g = round((baseRatio * formattedBaseColor.g ** 2 + toColorRatio * formattedToColor.g ** 2) ** 0.5);
outputColor.b = round((baseRatio * formattedBaseColor.b ** 2 + toColorRatio * formattedToColor.b ** 2) ** 0.5);
}


const blendedAlpha = formattedBaseColor.a * baseRatio + formattedToColor.a * toColorRatio;


outputColor.a = formattedToColor.a < 0 ? formattedBaseColor.a : blendedAlpha;


const hasAlpha = formattedBaseColor.a >= 0 || formattedToColor.a >= 0;
if (isRGBformat) {
return `rgb${hasAlpha ? "a" : ""}(${outputColor.r},${outputColor.g},${outputColor.b}${
hasAlpha ? `,${round(outputColor.a * 1000) / 1000}` : ""
})`;
}
return `#${(
outputColor.r * redSpace +
outputColor.g * greenSpace +
outputColor.b * blueSpace +
(hasAlpha ? round(outputColor.a * 255) : 0)
)
.toString(16)
// If no Alpha, we remove the last 2 hex digits
.slice(0, hasAlpha ? undefined : -2)}`;
};

还有一个笑话测试的集合

tint.test.ts

import { tint, toColorObject } from "./tint";


const rgbBlue = "rgb(20,60,200)";
const rgbaBlue = "rgba(20,60,200,0.67423)";
const hex6Cyan = "#67DAF0";
const hex3Pink = "#F3A";
const hex4Pink = "#F3A9";
const rbgBrown = "rgb(200,60,20)";
const rgbaBrown = "rgba(200,60,20,0.98631)";


describe("tint", () => {
describe("Logarithmic blending", () => {
describe("Shades", () => {
it("lightens rgb color", () => {
expect(tint(0.42, rgbBlue)).toEqual("rgb(166,171,225)");
});
it("darkens hex color", () => {
expect(tint(-0.4, hex3Pink)).toEqual("#c62884");
});
it("lightens rgba color", () => {
expect(tint(0.42, rgbaBrown)).toEqual("rgba(225,171,166,0.986)");
});
it("returns black with ratio -1", () => {
expect(tint(-1, rgbBlue)).toEqual("rgb(0,0,0)");
});
});
describe("converts color notation", () => {
it("converts from rgba to hexa", () => {
// expect(tint(0.42, color2, "c")).toEqual("#a6abe1ac");
expect(tint(0.42, rgbaBlue, { reformat: true })).toEqual("#a6abe1ac");
});
it("converts from hexa to rgba", () => {
// expect(tint(0, color6, "c", true)).toEqual("rgba(255,51,170,0.6)");
expect(tint(0, hex4Pink, { reformat: true })).toEqual("rgba(255,51,170,0.6)");
});
it("converts and returns white with ratio 1", () => {
expect(tint(1, hex3Pink, { reformat: true })).toEqual("rgb(255,255,255)");
});
});
describe("Blends two colors", () => {
it("blends rgba with rgba", () => {
expect(tint(-0.5, rgbaBlue, { toColor: rgbaBrown })).toEqual("rgba(142,60,142,0.83)");
});
it("blends rgba with rgb", () => {
expect(tint(0.7, rgbaBlue, { toColor: rbgBrown })).toEqual("rgba(168,60,111,0.674)");
});
it("blends hex with rgb", () => {
expect(tint(0.25, hex6Cyan, { toColor: rbgBrown })).toEqual("rgb(134,191,208)");
});
it("blends rgb with hex", () => {
expect(tint(0.75, rbgBrown, { toColor: hex6Cyan })).toEqual("#86bfd0");
});
});
});
describe("Linear Blending", () => {
describe("Shades", () => {
it("lightens rgb color", () => {
expect(tint(0.42, rgbBlue, { useLinear: true })).toEqual("rgb(119,142,223)");
});
it("darkens hex color", () => {
expect(tint(-0.4, hex3Pink, { useLinear: true })).toEqual("#991f66");
});
it("lightens rgba color", () => {
expect(tint(0.42, rgbaBrown, { useLinear: true })).toEqual("rgba(223,142,119,0.986)");
});
it("returns black with ratio -1", () => {
expect(tint(-1, rgbBlue, { useLinear: true })).toEqual("rgb(0,0,0)");
});
});
describe("converts color notation", () => {
it("converts from rgba to hexa", () => {
expect(tint(0.42, rgbaBlue, { reformat: true, useLinear: true })).toEqual("#778edfac");
});
it("converts from hexa to rgba", () => {
expect(tint(0, hex4Pink, { reformat: true, useLinear: true })).toEqual("rgba(255,51,170,0.6)");
});
it("converts and returns white with ratio 1", () => {
expect(tint(1, hex3Pink, { useLinear: true, reformat: true })).toEqual("rgb(255,255,255)");
});
});
describe("Blends two colors", () => {
it("blends rgba with rgba", () => {
expect(tint(-0.5, rgbaBlue, { toColor: rgbaBrown, useLinear: true })).toEqual("rgba(110,60,110,0.83)");
});
it("blends rgba with rgb", () => {
expect(tint(0.7, rgbaBlue, { toColor: rbgBrown, useLinear: true })).toEqual("rgba(146,60,74,0.674)");
});
it("blends hex with rgb", () => {
expect(tint(0.25, hex6Cyan, { toColor: rbgBrown, useLinear: true })).toEqual("rgb(127,179,185)");
});
it("blends rgb with hex", () => {
expect(tint(0.75, rbgBrown, { toColor: hex6Cyan, useLinear: true })).toEqual("#7fb3b9");
});
});
});
describe("Error handling", () => {
describe("When invalid hex color provided", () => {
it.each([1, 2, 5])("throws error if hex color has %s characters", (n) => {
const correlativeNumbers = Array.from(Array(n).keys()).join("");
expect(() => tint(0, `#${correlativeNumbers}`)).toThrow("Invalid hex color format");
});
});


describe("When ratio is not between -1 and 1", () => {
it("clamps ratio to -1", () => {
expect(tint(-43, rgbBlue)).toEqual("rgb(0,0,0)");
});
it("clamps ratio to 1", () => {
expect(tint(42, rgbBlue)).toEqual("rgb(255,255,255)");
});
});
});
});


describe("toColorObject function", () => {
it("should return a color object from hex", () => {
expect(toColorObject("#fff")).toEqual({
r: 255,
g: 255,
b: 255,
a: -1,
});
});
it("should return a color object from hex with alpha", () => {
expect(toColorObject("#fff6")).toEqual({
r: 255,
g: 255,
b: 255,
a: 0.4,
});
});
it("should return a color object from rgb", () => {
expect(toColorObject("rgb(255,255,255)")).toEqual({
r: 255,
g: 255,
b: 255,
a: -1,
});
});
it("should return a color object from rgba", () => {
expect(toColorObject("rgba(255,255,255,1)")).toEqual({
r: 255,
g: 255,
b: 255,
a: 1,
});
});
describe("Error handling", () => {
it("should throw error if invalid color provided", () => {
expect(() => toColorObject("foo")).toThrow("Invalid hex color format");
});
it("should throw error if invalid color provided", () => {
expect(() => toColorObject("invalid color")).toThrow("Invalid color format");
});
});
});

希望你喜欢。这很简单,但效果很好