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


504

プログラムで16進数の色を特定の量だけ明るくまたは暗くするために作業していた関数は次のとおりです。"3F6D2A"色(col)のような文字列と、amt明るくまたは暗くする量のbase10整数()を渡すだけです。暗くするには、負の数(つまり-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色空間を検討することをお勧めします。多くの言語には、変換用のライブラリがあります。私の経験では、特にオレンジの色合いは、暗くしたり明るくしたりするときに問題になる可能性があります。
Henrik

非常に良い点です。ただし、この質問の主な目的は、第一に、最速のランタイムと最小のサイズの式を見つけることであり、第二に、その精度を見つけることでした。したがって、なぜHSLなどへの変換を行わなかったのか。ここでは速度とサイズがより重要です。しかし、私のバージョン2の数式でわかるように。シェードにLERPを使用すると、シェードの範囲全体で心地よいオレンジになります。下のカラーチャートを見て、その色合いの範囲が実際の精度にかなり近くないかどうかお知らせください。
Pimp Trizkit 16

ここの構造と少し混乱しましたが、そうです、shadeColor1のオレンジレベルは非常に良いようです。
Henrik

笑、あなたはshadeColor2を意味します。あなたが話している構造は、答え自体の全体的なレイアウトですか?より明確にするためのヒントはありますか?
Pimp Trizkit

3
上記の#の関数には、最後の16進コードがゼロで始まる場合、先行ゼロが作成されないという問題が1つだけあります。たとえば、16進コードが#00a6b7の場合、#a6b7として出力されます。CSSとして使用すると機能しません。これを修正するには、戻り行を次のように置き換えます。var string = "000000" +(g |(b << 8)|(r << 16))。toString(16); return(usePound? "#": "")+ string.substr(string.length-6);
ラファエルレヴィ

回答:


877

さて、この答えはそれ自身の獣になりました。多くの新しいバージョン、それは長く愚かになっていた。この回答に対する多くの多大な貢献者に感謝します。しかし、大衆にとってそれをシンプルに保つために。この回答の進化のすべてのバージョン/履歴をgithubにアーカイブしました。そしてここで、StackOverflowで最新バージョンを使ってクリーンに始めました。このバージョンについてMike 'Pomax' Kamermansに特に感謝します。彼は私に新しい数学を与えました。


この関数(pSBC)は、16進数またはRGBのWebカラーを取ります。pSBC濃くしたり明るくしたり、2番目の色とブレンドしたり、そのまま渡すこともできますが、HexからRGB(Hex2RGB)またはRGBからHex(RGB2Hex)に変換できます。あなたが使用しているカラーフォーマットを知らなくてもすべて。

これは、特に多くの機能を考慮すると、本当に高速で、おそらく最速で実行されます。制作には長い時間がかかりました。私のgithubでストーリー全体をご覧ください。シェーディングまたはブレンドするための絶対的に最小かつ最速の方法が必要な場合は、以下のマイクロ関数を参照し、2ライナー速度のデーモンの1つを使用してください。激しいアニメーションには最適ですが、このバージョンでは、ほとんどのアニメーションに十分対応できます。

この関数は、ログブレンディングまたは線形ブレンディングを使用します。ただし、色を適切に明るくまたは暗くするためにHSLに変換されません。したがって、この関数の結果は、 HSLを使用するはるかに大きくて遅い関数とは異なります

jSFiddleとpSBC

github> pSBC Wiki

特徴:

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

コード:

// 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ライナー(IE。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:")");
}

詳細が必要ですか?githubで完全な記事を読んでください。

PT

(追記)もし誰かが他のブレンド方法の数学を持っているなら、共有してください。)


8
それを必要とする人のためのPHPバージョン:gist.github.com/chaoszcat/5325115#file-gistfile1-php
Lionel Chan

28
私が使用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
すみません、どうやらその点を逃してしまいました。おそらく2つの理由があります。最初の明らかなことは、私がMath.Roundを使用していて、正確なカラーリングのために10進数を使用できないことです(色は16進数で小数を持ちません)。たとえば、レッドチャネルがの場合、 どのラウンドにするかを8追加し10%ます。その後、奪うとあなたが得る。これは悪い例です。しかし、それはまだあなたが乗っているのであって、そうでないことを示しています。そのため、ここに示した例とまったく同じに切り捨てられない数値が含まれている可能性があります。8.899.09%98.181989.09%98.8
Pimp Trizkit、2015

122

私は非常にうまくいく解決策を作りました:

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、

不要です。テスト中のきれいな印刷のためだけに追加します。それを編集します。
Pablo、

非常に素晴らしい。ただし、どの色でも、100%明るくすると完全に白にならず、100%暗くすると常に黒になる必要がありますか?-100ではすべての色が黒になりますが、100(正)では完全に白にはなりません。
Kevin M

4
#ff0000、#00ff00、#0000ffなどの単色では機能しません
ひとり

黒い色で機能させるために、私はこのハックをやっただけです 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

21

エリックの答えに基づいた超シンプルなワンライナーです

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
「超シンプル」。
Andrew

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桁のみの場合、結果は次のようになります:たとえば、正しい値が '0a0a0a'のときは 'a0a0a'。私はあなたのリターンの代わりにこれを追加することで簡単に修正しました:

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
デバッグと賛辞をありがとう!私の主な質問である、より速い方法があるかどうかに対する答えではありません。すべての16進数を使用し、基本変換を使用していない可能性があります。Tho、私が正しいコード(+1)を持っているかどうかを教えてくれたと思います。残念ながら、この修正によりオーバーヘッドが大幅に増え(現在はtoStringを6回呼び出す)、KISSがわずかに減少しました。base16に変換する前に、base10の数が15以下かどうかを確認する方が速いでしょう。しかし、私は好きです!
Pimp Trizkit

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ハブを表示できます

または、別の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を指定する構文があります。


興味深いですが、16進数の文字列からRGBからHSLに変換する必要はありませんか?より複雑なようです。多分私は何かが欠けています。しかし、私はそれを行うためのKISSの方法、および可能な限り高速(実行時間)を探しています。理想的には、16進数ですべてを実行できるとしたら、それが最速です。しかし、私がここで開発したソリューションは、増分量を追加できるようにRGBに行くことを含みます。
Pimp Trizkit

はい、私はそれがより遅く、より複雑であると思います、そしてあなたが他のどこかでHSB変換へのRGBを使用しないなら、それはおそらく最も単純な解決策ではないでしょう。ただし、私自身は色の人ではありませんが、RGB値に追加するよりも正確です。それはあなたがどれだけ正確になりたいかによって異なります。
James Khoury、2011

あなたが言及する精度の損失はどうですか?[ウェブ]のすべての色がRGBなどで到達できないと思いますか?
Pimp Trizkit

先ほど言ったように、色についてはあまり知りません:wiki Color Theory
James Khoury

@Pimp Trizkit:これはあまり正確ではありません(そしてこれは私の理論です...私はカラーエキスパートではありません)。最初はその色がいくらあったとしても、各チャネルを同じ量シフトしているからです。私が考えることは、あなたが(割合で)互いに接近にチャンネルを持っているので、彩度を下げることになります。もちろん、オーバーフロー/アンダーフローが発生した場合は、どうしても避けられません。
Matthew Crumley、2011

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#を使用したことはありませんが、最後の3つの変数宣言はおかしいようです。intそして2 varsデータの同じタイプのため。
Pimp Trizkit 2014

4
C#のvarキーワードは、コンパイラーがコンパイル時に型を推測できるようにすることを意味します。上記の例では、intとvarは同じ型の変数を定義しています-int。これは、型名が長い場合、または匿名型を参照する場合に便利です。user1618171が2つの変数宣言スタイルを混合しているため、これは奇妙です。
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;}

クーリオ!私はそれpが範囲を持っていると思います0-100か?RGBで明るさを正しく定義する方法もわかりません。これはHSLの問題です。たとえば、#FF00FFより明るいです#FF0000か?もしそうなら、それはマゼンタが赤の2倍明るいことを意味します。したがって、純粋な赤のテストが使用されます。純粋な赤を渡し、#FF0000明るさを50%に設定し#FF4040ます。私は赤を50%の明るさにすることを推測したでしょう、私たちはより暗くなり、それがすでに完全に明るくなっているのを見る#800000でしょう#FF8080。ピンクは明るい赤ですか?または赤はすでに完全に明るいですか?
Pimp Trizkit 2018年

あなたは正しい-私はpが1-100の範囲でなければならないことを述べるべきだった!
TorbjörnJosefsson氏

#FF00FFの値は、赤チャネルで255、緑チャネルで0、青チャネルで255です。チャンネルの合計値が高いほど、色の明るさが高くなります。番号pは、新しい色を元の色の50%の明るさにしたいことを示しています。#FF4040が「50%できるだけ明るい赤」の正しい答えだと私は100%ではありません。(赤色チャネル、この場合、より低い値で)より暗い色合いを製造修正を必要とするであろう
TorbjörnJosefsson氏

はい、RGBの明るさについてのあいまいさを指摘していました。HSLに変換すると、Lチャネルは文字通り明るさになります。ここでの[個人的なメンタル]の問題は、私に#FF0000は完全に明るいということです。そして#FF4040、明るいですが明るくはありません...私にとって明るいとは、ピンクがそうであるように、白に近いということです。明るさはどれだけの明るさであり、完全な赤、つまり赤は完全に明るいです。したがって、#FF0000明るくすることはできません..むしろ軽量化することはできません。私は本当に色理論を知りません、すごい、私は本当に私のことを話しているだけです...
Pimp Trizkit

しかし、モニターの明るさを変更しても、レッズがピンクにならないことはわかっています... だからここがおそらく私が私のロジックを始めた場所です。
Pimp Trizkit 2018年

1

次の方法では、16進数(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の間のどこかでdouble値を渡します(-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バージョンの私の回答の終わりを確認してください。
Pimp Trizkit、2015年

0

そのjQuery依存関係を削除するために、優れたxcolorライブラリの移植を行いました。そこには、色を明るくしたり暗くしたりするなど、たくさんの機能があります。

実際、16進数を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)16進数をRGBに変換することは、完全に別の関数と見なすことができることに同意します。ここでの意図は、超高速で超小さかった回答(私のバージョン2を参照)と、16進数の色を明るくしたり暗くしたりすることでした...具体的には...スタンドアロンの自己完結型として関数。そのため、最終的な使用では、これは単純な単一の関数呼び出しになります。<続き>
Pimp Trizkit 2017年

2)そして、バージョン3のケースは、一般的な要望により、完全にスタンドアロンの自己完結型のユニバーサル機能をできるだけ速く、できるだけ小さくして、16進数またはRGBカラーを盲目的に取り、そのすべての色にすることができるという意図です。バリエーション。したがって、hexからRGBへの変換が必要です。<続き>
Pimp Trizkit 2017年

3)コードの簡単な分析。それははるかに遅く実行されるようであり、明らかに私のバージョン2よりもはるかに大きい(これは私のOPに対する本当の答えです。バージョン3は大衆向けでした)。公平を期すために、私はこのコードを、変換を行わず、乾燥に関するあなたの意見に答えるように見える私のRGBバージョン2と比較する必要があります。そして、正直に言えば、あなたのポートは、私のヘックス用の2つのライナーよりも理解するのが簡単ではありません。したがって、その乾燥機でありながら、実際にはそれほど単純ではありません。(乾燥は理解力にあまり役立ちませんでした)<続き>
Pimp Trizkit

4)必要に応じて、RGBバージョン2は変換なしの2ライン関数です。私の元のOPに対する私の特定のソリューションは、16進数を望んでいました。そのため、バージョン2には2種類のタイプがありますが、乾燥と16進変換についてのポイントを説明したので、バージョン3に焦点を当てています。バージョン3はかなり後のバージョンです。バージョン2が普及した後だけです。<続き>
Pimp Trizkit 2017年

5)乾燥は一般的に普遍性を助けることに同意します。そして、ほとんどの場合、理解力のために。残念ながら、この例ではコストがかかります。これらのコストは、はるかに大きく、一見かなり遅く、スタック(再帰的な性質を持つ)とグローバル(v2と比較して2つの関数)の両方でより多くのメモリを使用するようです。
Pimp Trizkit 2017年

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またはそれに似たものを使用しているように見えます。残念ながら、それは1つのチャネルからのみであり、その同じ値がすべてのチャネルで使用されます。これは正しくありません。より高い精度を得るには、各チャネルを個別にLERPする必要があります。この質問への私の答えがするように。さらに、より小さくて高速で、エラーをチェックしてRGBを処理し、変換を実行します
Pimp Trizkit '29

使用例:varyHue( "6e124c"、77)。最初の引数は16進数の色で、2番目はパーセンテージの変化です。正のパーセンテージの変化は陰影を付け(暗く)、負の値は結果を色付け(明るく)します。このページにたどり着く数時間前の最初の試みとして、私はこのルーチンを書き、それを単に関心事として投稿しました。私はあなたの努力をよりよくしなければならないこと、またはそうする前にあなたの承認を必要とすることを知りませんでした。他の人に言及することなく、完全に自分自身の作品。LERPについて聞いたことがないので、チェックしてみます。提案に感謝します。
user2655360 2017

もちろん、何もする必要はありません。そして私たちはみなあなたの努力に感謝します!私の最初の主な懸念は、最初にリストされたものでした。投票を獲得できるように、回答を手助けしようとしています。(使用法を示し、それがどのように機能するかの説明など)他のことは明らかに誰もがさらに知識を深めるのに役立つ迅速な分析です。少しアグレッシブに見えた場合は申し訳ありません。しかし、別の提案は#、16進数の色を受け入れるようにすることです。「承認」と思われた場合はごめんなさい...私はそれを査読として見ました。誰かにコードを分析してほしくない、またはフィードバックを提供したくない場合は、お詫び申し上げます。
Pimp Trizkit 2017

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進数ではない値(16 = 0xF)と(8 = 0x8)が原因で機能しませんでしたが、8つの位置に色が表示されましたが、非常にうまく機能しています
Nassim

FFから増やしてみましたか?(たとえば、0xFFFFFF + 0x000004から):コードは増加せずに、その最大値(たとえば、0x1000003に)をロールオーバーします。特に、上位2つのカラーコンポーネントを00に設定します

正解です。fffと000に関する制限の入力を除いて、正常に機能します。
Nassim
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.