以编程方式使十六进制颜色(或rgb和混合颜色)变亮或变暗


503

这是我正在使用的功能,用于以编程方式使十六进制颜色变亮或变暗特定数量。只需传入一个"3F6D2A"颜色字符串(col)和一个amt以10为底的整数()来表示要变亮或变暗的数量。要变暗,请输入负数(即-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);

我想我的主要问题是,我在这里正确吗?这不包括某些(正常)情况吗?


1
如果在修改颜色时没有得到预期的结果,我建议您研究更接近于人类视觉的LAB颜色空间。许多语言都有用于转换的库。以我的经验,当变暗或变亮时,尤其是橙色阴影可能会出现问题。
亨里克

很好的一点。但是,此问题的主要目的是首先找到运行速度最快和尺寸最小的公式...其次才是准确性。因此,为什么我不处理转换为HSL等问题。在这里速度和大小更重要。但是,正如您在我的公式的版本2中所看到的。使用LERP阴影将在整个阴影范围内产生令人愉悦的橙色。看一下下面的颜色表,让我知道该阴影范围是否还远远不能达到实际精度。
皮条客Trizkit '16

我对这里的结构有些困惑,但是您是对的,shadowColor1的橙色级别似乎非常好。
Henrik

大声笑,你的意思是shadeColor2。我猜您正在谈论的结构是答案本身的总体布局?有什么提示需要更清楚吗?
皮条客Trizkit '16

3
上面带有#的函数中只有一个问题是,如果最终的十六进制代码以零开头,则不会创建前导零。例如,如果十六进制代码为#00a6b7,则将其输出为#a6b7,如果用作css,它将无法正常工作。您可以通过以下方式来替换返回行:var string =“ 000000” +(g |(b << 8)|(r << 16))。toString(16); return(usePound?“#”:“”)+ string.substr(string.length-6);
拉斐尔·利维

Answers:


876

好吧,这个答案已经变成了自己的野兽。许多新版本,它变得愚蠢了很长时间。非常感谢对此答案做出的所有贡献。但是,为了使它简单易行。我将该答案的所有版本/历史记录存档到github中。并以最新版本在StackOverflow上重新启动。特别感谢Mike'Pomax'Kamermans这个版本。他给了我新的数学。


此功能(pSBC)将采用十六进制或RGB网页颜色。pSBC可以使它变暗或变亮,或将其与第二种颜色混合,也可以将其直接传递但从十六进制转换为RGB(Hex2RGB)或从RGB转换为十六进制(RGB2Hex)。所有这些甚至都不知道您使用的是哪种颜色格式。

这运行得非常快,可能是最快的,特别是考虑到它的许多功能。制作时间很长。在我的github上查看整个故事。如果要使用绝对最小和最快的方式着色或混合,请参阅下面的微功能并使用2线速度恶魔之一。它们非常适合用于激烈的动画,但是此处的此版本对于大多数动画而言足够快。

此功能使用对数混合或线性混合。但是,它不能转换为HSL来适当地使颜色变暗或变暗。因此,此函数的结果将不同于使用HSL的更大,更慢的函数。

jsFiddle与pSBC

github> pSBC维基

特征:

  • 自动检测并接受字符串形式的标准十六进制颜色。例如:"#AA6622""#bb551144"
  • 自动检测并接受字符串形式的标准RGB颜色。例如:"rgb(123,45,76)""rgba(45,15,74,0.45)"
  • 按百分比将颜色着色为白色或黑色。
  • 按百分比将颜色混合在一起。
  • Hex2RGB和RGB2Hex是否同时或单独转换。
  • 接受格式为#RGB(或#RGBA)的3位数(或4位数,带alpha)十六进制颜色代码。它将扩展它们。例如:"#C41"变为"#CC4411"
  • 接受和(线性)混合Alpha通道。如果c0(从)颜色或c1(至)颜色具有Alpha通道,则返回的颜色将具有Alpha通道。如果两种颜色都具有一个Alpha通道,则返回的颜色将是使用给定百分比的两个Alpha通道的线性混合(就像它是正常颜色通道一样)。如果两种颜色中只有一种具有Alpha通道,则该Alpha会直接传递到返回的颜色。这样可以在保持透明度级别的同时混合/着色透明颜色。或者,如果透明度级别也要混合,请确保两种颜色都具有Alpha。着色时,它将直接通过Alpha通道。如果您想要基本阴影也可以对Alpha通道进行阴影处理,请使用rgb(0,0,0,1)rgb(255,255,255,1)作为您的c1(至)颜色(或它们的十六进制等效项)。对于RGB颜色,返回的颜色的alpha通道将四舍五入到小数点后三位。
  • 使用混合时,RGB2Hex和Hex2RGB转换是隐式的。不论c0颜色(来源);c1如果存在,返回的颜色将始终为(至)颜色的颜色格式。如果没有c1颜色,则将其'c'作为c1颜色传递,它将着色并转换为任何c0颜色。如果仅需要转换,则0也以百分比(p)形式传递。如果c1省略颜色或string传递非颜色,则将不会转换。
  • 辅助功能也被添加到全局中。pSBCr可以传递十六进制或RGB颜色,并返回包含此颜色信息的对象。其格式为:{r:XXX,g:XXX,b:XXX,a:X.XXX}。其中.r.g和的.b范围是0到255。并且当没有alpha时:.a是-1。否则:.a范围为0.000至1.000。
  • 对于RGB输出时,它输出rgba()rgb()当与alpha通道的色通入c0(从)和/或c1(至)。
  • 轻微错误检查已添加。这不是完美的。它仍然可能崩溃或产生乱码。但是,它将捕获一些东西。基本上,如果结构在某些方面是错误的,或者百分比不是数字或超出范围,它将返回null。例如:pSBC(0.5,"salt") == null,它认为#salt是有效的颜色。删除最后四行return null;以删除此功能并使它变得越来越快。
  • 使用日志混合。传递truefor 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}

下图将帮助显示两种混合方法的区别:


微功能

如果您确实想要速度和大小,则必须使用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

(请注意,如果有人对另一种混合方法有所了解,请分享。)


8
适合那些需要它的人的PHP版本:gist.github.com/chaoszcat/5325115#file-gistfile1-php
Lionel Chan

27
我用TinyColor -tinycolor.darken(color,amount);
FWrnr

4
很棒的帖子... :) ...刚刚创建了它的Swift扩展:gist.github.com/matejukmar/1da47f7a950d1ba68a95
Matej Ukmar

2
这是更新的shadeColor2版本的PHP版本:function shadeColor2($color, $percent) { $color = str_replace("#", "", $color); $t=$percent<0?0:255; $p=$percent<0?$percent*-1:$percent; $RGB = str_split($color, 2); $R=hexdec($RGB[0]); $G=hexdec($RGB[1]); $B=hexdec($RGB[2]); return '#'.substr(dechex(0x1000000+(round(($t-$R)*$p)+$R)*0x10000+(round(($t-$G)*$p)+$G)*0x100+(round(($t-$B)*$p)+$B)),1); }
Kevin M

2
对不起,我显然没注意这一点。可能有两个原因。第一个也是显而易见的是,我使用Math.Round,并且您没有使用十进制数字进行准确的着色(颜色没有十六进制的小数)。例如,如果“红色”通道为 8,则添加10%8.8四舍五入为9。然后从9.09%拿走9,你会得到8.1819。四舍五入,8所以这是一个不好的例子。但它仍然说明你正在服用9.09%9,而不是8.8。因此,其中有些数字可能与我在此示例中的数字不完全相同。
Pimp Trizkit 2015年

121

我提出了一个对我来说很好的解决方案:

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

4
不错,我喜欢这个百分比!+1芹苴,我也可能会做R = ((R<255)?R:255).toString(16);那么R = R.length==1 ? "0"+R : R速度。而且我不确定toUpperCase的意义吗?
Pimp Trizkit 2012年

没必要 我只在测试时添加漂亮的打印件。我将对其进行编辑。
巴勃罗(Pablo)

非常好。但是,无论什么颜色,浅100%的浅色都不应变成全白色,而深100%的深浅则始终是黑色?看起来-100确实会使任何颜色变成黑色,但是100(正)却没有使其变成完全白色。
凯文M

4
纯色不起作用如#FF0000,#00FF00,#0000FF
Hitori

为了使它与黑色兼容,我只是做了这个技巧 var R = parseInt(color.substring(1, 3), 16) var G = parseInt(color.substring(3, 5), 16) var B = parseInt(color.substring(5, 7), 16) if (R == 0) R = 32; if (G == 0) G = 32; if (B == 0) B = 32;
Irfan Raza

20

这是一个基于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"

7
“超级简单”。
安德鲁

5

这是我根据您的功能使用的。我更喜欢使用百分比而不是百分比,因为它对我来说更直观。

例如,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;

}

发现这比顶部答案有用得多,因为顶部答案使我的颜色非常浓而不是更深。干杯埃里克
蠕虫

4

我尝试了您的函数,但有一个小错误:例如,如果某个最终的'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;

也许它不是很好,但是可以完成工作。功能强大,顺便说一句。正是我所需要的。:)


1
感谢您的调试和赞美!不幸的是,这不是对是否有更快方法的答案,这是我的主要问题。就像可能使用所有十六进制而不进行基本转换一样。星期四,我想您确实告诉过我是否有正确的代码(+1)。不幸的是,此修复程序增加了更多的开销(现在您调用toString 6次),而KISS却稍微减少了。也许在进行base16转换之前检查base10数字是否为15或更小会更快。但我喜欢!
Pimp Trizkit 2012年

4

您是否考虑过rgb> hsl转换?然后只是上下移动光度?那就是我要走的路。

快速浏览一些算法后,获得了以下站点。

PHP:http//serennu.com/colour/rgbtohsl.php

Javascript:http//mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript

编辑上面的链接不再有效。您可以查看页面来源要点的 git hub

另外,另一个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. 您具有hslToRgb和的功能rgbToHsl
  2. 参数colorValue是形式为#RRGGBB的字符串

尽管如果我们讨论CSS,则有一种语法可为IE9 / Chrome / Firefox 指定hsl / hsla


有趣,但是我是否不必将十六进制字符串从rgb转换为hsl?似乎更复杂。也许我想念一些东西。但是,我正在寻找一种尽可能快(执行时间)的KISS方式。我的理想状态是,如果我能以十六进制完成所有操作,那将是最快的。但是,我在这里开发的解决方案涉及到rgb以便能够增加增量。
Pimp Trizkit 2011年

是的,我认为它会更慢,更复杂,如果您不在其他任何地方使用rgb到hsl转换,那么它可能不是最简单的解决方案。但是,尽管我自己不是一个有色人种,但它比添加rgb值更准确。这一切都取决于我猜想达到的精度。
James Khoury

您提到的准确性损失是什么?我假设您是说用rgb或其他东西无法获得所有[web]颜色?
Pimp Trizkit 2011年

正如我说的,我对颜色了解不多:Wiki Color Theory
James Khoury

@Pimp Trizkit:它的准确性较差,因为(这只是我的理论,我不是色彩专家)您要为每个通道移动相同的数量,无论开始时有多少颜色。一世认为这将导致饱和度降低,因为您使通道彼此更靠近(按百分比)。当然,如果您溢出/下溢,那还是不可避免的。
马修·克鲁姆利

2

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

5
以前从未使用过C#,但看起来最后三个变量声明很奇怪。对于相同类型的数据,一个int和两个vars
Pimp Trizkit 2014年

4
C#中的var关键字表示让编译器在编译时推断类型。因此,在上面的示例中,int和var定义了相同类型的变量-int。如果您使用的是较长的类型名称,或者要引用匿名类型,这将很有用。这很奇怪,因为user1618171混合了两种变量声明样式-可能是拼写错误。
Daniel James Bryars 2015年

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

Coolio!我认为p有范围0-100吗?我什至不知道如何正确定义RGB中的亮度,这就是HSL。例如,#FF00FF比还要亮#FF0000?如果是这样,则意味着洋红色的亮度是红色的两倍。因此,使用纯红色测试。传递纯红色#FF0000,将其设置为50%的亮度,我们得到#FF4040,对吗?我猜想使红色达到50%的亮度,我们会变得更暗,因为看到它已经足够亮了。#800000或达到150%的亮度#FF8080。粉色是明亮的红色吗?还是红色已经完全亮了?
皮条客Trizkit

您是对的-我应该提到p必须在1-100的范围内!
TorbjörnJosefsson拥有

#FF00FF在红色通道中的值为255,在绿色通道中的值为0,在蓝色通道中的值为255。通道中的组合值越高,颜色的亮度越高。数字p表示我们希望新颜色的亮度是原始颜色的50%。我不是100%正确地回答#FF4040“ 50%越红越好”。产生暗色调(有,在这种情况下,在红色通道较低的值)将要求修改
TorbjörnJosefsson拥有

是的,我只是指出谈论RGB亮度时的歧义。如果转换为HSL,则该L通道实际上是亮度。在这里,我的[个人心理]问题是,对我而言,这#FF0000是完全明智的。而且#FF4040更亮,但不更亮。...对我来说,更浅意味着更接近白色,就像粉红色一样。亮度就是它的亮度,它的全红色(即红色)是全亮的。因此,#FF0000不能变得更亮..而是..更亮...也许我只是个怪胎,哈哈!我真的不了解色彩理论,太好了,我真的只是在说我的...
Pimp Trizkit

但是我知道,当我改变显示器的亮度时,红军不会变成粉红色……对我来说。所以这可能就是我开始逻辑的地方。
皮条客Trizkit

1

使用以下方法可以使十六进制(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%;

0

如何在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

这是Pablo答案的php版本。不幸的是,它比最终解决方案更长,更慢,并且无法准确地淡化颜色。确实会使它们变暗。用纯红色(#FF0000)进行测试,应减亮25%(#FF4040)。请查看我答案的结尾,以了解最终解决方案v2的Kevin M的PHP版本。
2015年

0

我做了一个优秀的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了解更多信息。


我还没有分析与我的OP有关的代码(速度/大小/准确性)。但是,在开始阅读时,有一些评论需要做:1)我同意将十六进制转换为RGB可以看作是一个完全独立的函数。如果我的问题打算通过干函数来解决,那不是必须的。这里的目的是要有一个答案(请参阅我的版本2),它超级快,非常小(两行!),并且使十六进制颜色变亮和变暗……特别是……作为独立的独立设备功能。这样,在最终使用时,它将是一个简单的单个函数调用。<续>
Pimp Trizkit

2)根据普遍需求,第3版的情况是希望拥有一个完全独立的,自包含的通用功能,该功能尽可能快且尽可能小,可以盲目采用十六进制或RGB颜色,并且在所有颜色中变化。因此,需要将十六进制转换为RGB。<cont。>
Pimp Trizkit'1

3)简单分析一下代码。看来它的运行速度要慢得多,并且显然比我的版本2大得多(这是对我的OP的真正回答;版本3是针对大众的)。公平地说,我应该将此代码与我的RGB版本2进行比较,后者没有进行转换,似乎可以回答您有关干燥的问题。说实话,您的端口比我的2进制内衬简单得多。因此,虽然是干燥机,但实际上并没有那么简单。(干燥对于提高理解能力没有多大帮助)<续>
Pimp Trizkit

4)如果需要,我的RGB版本2是无转换2线功能。我对原始OP的特定解决方案需要十六进制。这就是为什么版本2有两种不同类型的原因。但是您提到了干燥和十六进制转换的要点,因此我们现在真正专注于版本3。仅在第2版流行之后。<cont。>
Pimp Trizkit

5)虽然我同意干燥通常有助于普遍性。并且在大多数情况下,为了提高理解能力。不幸的是,在这个例子中,它是有代价的。这些代价是,它在堆栈上(具有类似递归的性质)和全局内存(2个功能;与v2相比)要大得多,看似要慢得多,并且似乎要使用更多的内存。
皮条客Trizkit '17

0

我一直希望能够产生色彩/阴影,这是我的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);
    }
};

一些用法示例会有所帮助。关于此版本为何优于其他版本的一些解释。这个版本的运行速度似乎慢得多。而且更长。而且它似乎没有正确地阴影。看起来您在使用LERP或类似的东西。。不幸的是,它仅来自一个通道,然后在所有通道中使用相同的值。这是不对的,为了获得更高的精度,您应该单独对每个通道进行LERP。正如我对这个问题的回答一样。加上其更小,更快,检查错误和处理rgb以及进行转换,我可以继续进行下去
Pimp Trizkit

一个用法示例:variantHue(“ 6e124c”,77),其中第一个参数是十六进制颜色,第二个参数是百分比变化。正百分比变化将阴影(变暗),负值会使着色(变亮)。我在进入此页面前几个小时就写了例程,这是我的第一次尝试,只是出于兴趣而已。我不知道我必须加倍努力,或者在获得批准之前需要您的批准。这完全是我自己的工作,没有提及其他任何人。我没有听说过LERP,我会检查一下,谢谢您的建议。
user2655360

呵呵,当然,您不必做任何事!我们都感谢您的努力!我首先关注的是最先列出的那些。尝试帮助您解决问题,以便获得投票。(显示用法,并解释其工作原理等)其他内容显然是一种快速分析,有助于进一步了解每个人。抱歉,如果它看起来有点激进。但是另一个建议是使其接受#十六进制颜色。对不起,如果它看起来像..“批准” ...我将其视为同行评审。如果您不希望别人分析您的代码或提供反馈,我深表歉意。
皮条客Trizkit”

0

您的方法还可以:)我将您最短的版本稍作简化(有关饱和度控制,请点击此处

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

以及带有#和颜色范围检查的版本


0

我也做了一个简单的包装。我使用了IR ^ 3的凸度,当然RGB值在IN ^ 3中,这不是凸度,所以这不是很完美

为了变暗,我使用了以下内容

for (let i = 0; i < rgb.length; i++) {
   rgb[i] = Math.floor(rgb[i] - ratio * rgb[i]);
}

为了减轻

for (let i = 0; i < rgb.length; i++) {
   rgb[i] = Math.ceil(rgb[i] + ratio * (255 - rgb[i]));
}

这是包 https://github.com/MarchWorks/colortone

演示版 https://colortone.now.sh/

用我做事的方式,如果您通过-1的比率,您将得到黑色,如果该比率为1,则最终将是白色。通过0会因为该比率不会改变颜色


0

我在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;
    }

你转换的“非工作”功能,不带前导零的处理,可以去上面FF的总和,=修改上面的颜色分量...
B.围棋

当我尝试该函数时,由于非十六进制值(16 = 0xF)和(8 = 0x8)而无法正常工作,并在8个位置给出了颜色,但现在效果很好
Nassim,

您尝试从FF增加吗?(从0XFFFFFF +是0x000004说):在该最大值代码辊(说0x1000003),而不是不增加,尤其是设置2种上的颜色成分为00这是他们能做的最大的变化...
B.进入

您是对的,谢谢您的发言,除了输入限制范围fff和000可以正常使用。
Nassim
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.