プログラムで16進数カラー(またはRGB、ブレンドカラー)を明るくしたり暗くしたりする 質問する

プログラムで16進数カラー(またはRGB、ブレンドカラー)を明るくしたり暗くしたりする 質問する

これは、プログラムによって 16 進カラーを特定の量だけ明るくしたり暗くしたりするために私が取り組んでいた関数です。"3F6D2A"色として のような文字列 ( col) を渡し、明るくしたり暗くしたりする量として 10 進整数 ( 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);

私の主な質問は、私が正しいかどうかです。これはほとんどの (通常の) 状況を網羅しているのではないですか。もしそうなら、これを行う最も速くて小さな方法は何ですか。アニメーションや小さな環境で使用したいので、速度がここで最も重要な要素の 1 つであり、サイズが 2 番目、正確性が 3 番目、読みやすさですか。えっ? 要件リストにはありません (申し訳ありませんが、皆さんの半分が今涙を流していることはわかっています)。

ベストアンサー1

さて、この回答は独自のものになりました。多くの新しいバージョンがあり、長くなりすぎていました。この回答に貢献してくれた多くの方々に感謝します。しかし、大衆のためにシンプルにするために、この回答の進化のすべてのバージョンと履歴を私のギットハブ. そして、StackOverflowで最新バージョンを最初からやり直しました。マイク・ポマックス・カマーマンスこのバージョンでは、彼は私に新しい計算を教えてくれました。


この関数 ( pSBC) は、HEX または RGB の Web カラーを受け取ります。pSBCこれを暗くしたり明るくしたり、2 番目の色とブレンドしたり、そのまま渡したり、HEX から RGB (Hex2RGB) または RGB から HEX (RGB2Hex) に変換したりすることもできます。どのカラー形式を使用しているかさえ知らなくても、すべて実行できます。

これは本当に速く、特にその多くの機能を考えるとおそらく最速です。開発には長い時間がかかりました。私のストーリー全体をご覧ください。ギットハブシェーディングやブレンドを可能な限り最小かつ最速で行うには、以下のマイクロ関数を参照して、2 行のスピード デーモンのいずれかを使用します。これらは激しいアニメーションに最適ですが、ここで紹介するバージョンでもほとんどのアニメーションには十分な速度です。

この関数は、ログ ブレンディングまたはリニア ブレンディングを使用します。ただし、色を適切に明るくしたり暗くしたりするために HSL に変換するわけではありません。したがって、この関数の結果は、HSL を使用するはるかに大きくて遅い関数 (比較すると、実際にはかなり大きい TinyColor など) とは異なる場合があります。

pSBC での jsFiddle

github > pSBC ウィキ

特徴:

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

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


マイクロ機能

本当にスピードとサイズが必要な場合は、HEX ではなく RGB を使用する必要があります。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

(追伸: 別のブレンド方法の計算を知っている方がいたら、ぜひ共有してください。)

おすすめ記事