1. ホーム
  2. javascript

[解決済み] プログラムによって、16進数の色を明るくしたり暗くしたりする(または、rgb、およびブレンドカラー)。

2022-03-16 10:03:06

質問

プログラム上で16進数の色を特定の量だけ明るくしたり暗くしたりする関数を作りました。以下のような文字列を渡すだけです。 "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);
}

OK、これで2、3行ではなくなりましたが、はるかにシンプルに見えますし、"#"を使わず、範囲外の色をチェックする必要がなければ、たった2、3行で済みますね。

もし、"#"を使わないのであれば、次のようなコードで追加すればよいでしょう。

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

私の一番の疑問は、ここで私が正しいかどうかということです。これは、いくつかの(通常の)状況を包含していないのでしょうか?

どのように解決するのですか?

さて、この回答は、それ自体が獣になってしまいました。多くの新バージョンが登場し、バカみたいに長くなってしまったのです。この回答への素晴らしい多くの貢献者の皆さんに感謝します。しかし、大衆のためにそれをシンプルに保つために。この答えの進化のすべてのバージョン/履歴を、私の ギズブ . そして、このStackOverflowで最新バージョンできれいにやり直しました。特別な感謝を捧げます。 マイク'ポマック'カママンズ このバージョンでは 彼は私に新しい数学を教えてくれました。


この機能( pSBC ) は、HEX または RGB のウェブカラーを受け取ります。 pSBC また、そのまま通過させることもできますが、HexからRGB(Hex2RGB)、RGBからHex(RGB2Hex)への変換も可能です。どのような色形式を使用しているかを知ることなく、すべての変換が可能です。

これは本当に速く、特にその多くの機能を考慮すると、おそらく最速でしょう。長い時間をかけて作られたものです。その全貌は、私の ギズブ . もしあなたが、シェーディングやブレンドをするために、絶対に最小で最速の方法を望むなら、以下のMicro Functionsを参照して、2-liner speed demonsの1つを使用してください。これらは激しいアニメーションに最適ですが、ここにあるバージョンはほとんどのアニメーションに十分な速さです。

ログブレンディングまたはリニアブレンディングを使用します。しかし、色を適切に明るくしたり暗くしたりするための HSL 変換は行いません。そのため この関数の結果は異なります HSLを使用する、より大規模でより低速な関数とは異なります。

jsFiddle with pSBC

github > pSBC ウィキ

特徴

  • 標準的なHexカラーを文字列として自動検出し、受け付けます。例えば "#AA6622" または "#bb551144" .
  • 標準的なRGBカラーを文字列の形で自動検出し、受け入れる。例えば "rgb(123,45,76)" または "rgba(45,15,74,0.45)" .
  • 色をパーセント単位で白または黒にシェーディングします。
  • 色を混ぜ合わせる割合を指定します。
  • Hex2RGBとRGB2Hexの同時変換、または単独変換が可能です。
  • 3桁(またはアルファ付き4桁)のHEXカラーコード、#RGB(または#RGBA)形式を受け付けます。それはそれらを展開します。例えば "#C41" は次のようになります。 "#CC4411" .
  • アルファチャンネルを受け入れ、(リニア)ブレンドする。もし c0 (元の)色または c1 (to) の色がアルファチャンネルを持つ場合、返される色もアルファチャンネルを持つことになります。両方の色にアルファチャンネルがある場合は、2 つのアルファチャンネルを指定した割合で線形混合した色が返されます (通常のカラーチャンネルと同じように)。2 つの色のうち一方にしかアルファチャンネルがない場合は、 このアルファチャンネルがそのまま返される色に渡されます。これにより、透明度を維持したまま、透明な色をブレンドしたりシェーディングしたりすることができます。あるいは、透明度もブレンドしたい場合は、両方の色にアルファを持たせてください。シェーディングを行う場合、アルファチャンネルをそのまま通過させます。アルファチャンネルもシェーディングする基本的なシェーディングが必要な場合は、次のようにします。 rgb(0,0,0,1) または rgb(255,255,255,1)c1 (to)の色(またはその16進数相当)を指定します。RGBカラーの場合、返されるカラーのアルファチャンネルは小数点以下3桁に丸められます。
  • RGB2HexおよびHex2RGBの変換は、ブレンド使用時には暗黙のうちに行われます。にもかかわらず c0 (from) の色で、返される色は常に、(from) の色形式である。 c1 (行き先) の色がある場合は、その色。がない場合は c1 (to)カラーを渡します。 'c'c1 を指定すると、その色に陰影をつけて変換します。 c0 の色です。もし変換だけが必要であれば 0 をパーセントで指定します ( p ) もあります。もし c1 の色が省略された場合、または非 string が渡されると、変換されません。
  • 2次関数も同様にグローバルに追加されます。 pSBCr にはHexまたはRGBの色を渡すことができ、この色情報を含むオブジェクトを返します。その形式は {r: XXX, g: XXX, b: XXX, a: X.XXX}のような形式です。ここで .r , .g および .b は0〜255の範囲です。また、アルファがない場合は .a は-1です。それ以外の場合は .a は 0.000 から 1.000 の範囲である。
  • RGB出力の場合は、以下のように出力されます。 rgba() オーバー rgb() にアルファチャンネルを持つ色が渡された場合、その色は c0 (から)および/または c1 (宛先)を指定します。
  • マイナーエラーチェックが追加されました。完璧ではありません。まだクラッシュしたり、意味不明な文章を作成したりすることがあります。しかし、いくつかのものを捕らえることができます。基本的には、もし構造が何らかの形で間違っていたり、パーセンテージが数字でなかったり、スコープ外であったりした場合、それは null . 例として pSBC(0.5,"salt") == null と思っているところへ #salt は有効な色です。で終わる4つの行を削除してください。 return null; を使うと、この機能が削除され、高速化・小型化されます。
  • ログブレンディングを使用します。パス true に対して l (第4パラメータ)を使用すると、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}


下の図は、2つのブレンド方法の違いを示すのに役立ちます。


マイクロ機能

もし本当にスピードとサイズを求めるのであれば、HEXではなくRGBを使うしかないでしょう。HEXは書き込みが遅すぎるし、単純な2桁のコードにしては種類が多すぎます(つまり、3桁、4桁、6桁、8桁のHEXコードがあり得る)。また、エラーチェックやHEX2RGB、RGB2HEXなどの機能を犠牲にする必要があります。また、カラーブレンドの計算には特定の関数(以下の関数名に基づいて)を選択する必要があり、シェーディングやブレンディングが必要な場合もあります。これらの関数はアルファチャンネルをサポートしています。そして、両方の入力色がアルファを持つ場合、それらを線形にブレンドします。2色のうち1色にしかアルファがない場合は、それをそのまま結果の色に渡します。以下に、信じられないほど高速で小さなライナー関数を2つ紹介します。

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


もっと詳しく知りたい方はこちら の記事全文を読む ギズーブ .

PT

(追記)別の配合方法の計算方法をご存知の方がいらっしゃいましたら、教えてください(笑)