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

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

有趣的灯光 上校(16号); return键(col . & 0x0000FF) + amt) | ((((col > > 8) & 0x00FF) + amt) < < 8) | (16 (col > >) + amt) < < 16) . toString (16); 的 / /测试 控制台(照明颜色(“3F6D2A”,40);

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

函数 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 新颜色 = g |(b << 8) |(第16<<条); 返回 newColor.toString(16); } 测试 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);

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


你考虑过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;
}

这样的假设:

你有hslToRgb和rgbToHsl函数。 参数colorValue是一个#RRGGBB形式的字符串

虽然我们讨论的是css,但在IE9/Chrome/Firefox中有指定hsl/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;  

    R = Math.round(R)
    G = Math.round(G)
    B = Math.round(B)

    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上重新开始。特别感谢Mike 'Pomax' Kamermans制作了这个版本。他给了我新的数学。


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

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

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

摆弄邮政储蓄银行 github > pSBC Wiki

特点:

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 the c1 (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 use rgb(0,0,0,1) or rgb(255,255,255,1) as your c1 (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 the c1 (to) color, if one exists. If there is no c1 (to) color, then pass 'c' in as the c1 color and it will shade and convert whatever the c0 color is. If conversion only is desired, then pass 0 in as the percentage (p) as well. If the c1 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() over rgb() when a color with an alpha channel was passed into c0 (from) and/or c1 (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 with return null; to remove this feature and make it faster and smaller. Uses Log Blending. Pass true in for l (the 4th parameter) to use Linear Blending.

代码:

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

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


微型功能

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

想了解更多信息?在github上阅读全文。

PT

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


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的倍数)。


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

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

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