Schiarisci o scurisci a livello di codice un colore esadecimale (o rgb e fondi colori)


504

Ecco una funzione su cui stavo lavorando per schiarire o scurire a livello di codice un colore esadecimale di una quantità specifica. Basta passare una stringa come "3F6D2A"per il colore ( col) e un numero intero base10 ( amt) per la quantità da schiarire o scurire. Per scurire, passa un numero negativo (es-20 ).

Il motivo per cui ho fatto questo è stato a causa di tutte le soluzioni che ho trovato, finora, sembravano complicare eccessivamente il problema. E ho avuto la sensazione che si potesse fare con solo un paio di righe di codice. Per favore fatemi sapere se trovate qualche problema, o avete qualche aggiustamento da fare per accelerarlo.

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

Per lo sviluppo, utilizzare qui una versione più semplice da leggere:

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

E infine una versione per gestire i colori che possono (o meno) avere "#" all'inizio. Inoltre regolazione per valori di colore errati:

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, quindi ora non è solo un paio di linee, ma sembra molto più semplice e se non stai usando il "#" e non hai bisogno di controllare i colori fuori dal raggio, sono solo un paio di linee.

Se non usi "#", puoi semplicemente aggiungerlo in codice come:

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

Immagino che la mia domanda principale sia: ho ragione qui? Questo non comprende alcune (normali) situazioni?


1
Se non si ottengono i risultati previsti quando si modificano i colori, suggerisco di esaminare lo spazio colore LAB, che è più vicino alla visione umana. Molte lingue hanno librerie per la conversione. Nella mia esperienza, soprattutto le sfumature di arancione possono essere problematiche quando si scuriscono o schiariscono.
Henrik

Ottimo punto Tuttavia, lo scopo principale di questa domanda era quello di trovare, in primo luogo, il tempo di esecuzione più veloce e la formula delle dimensioni più piccole ... e, in secondo luogo, la sua precisione. Quindi, perché non ho affrontato la conversione in HSL o altro. Qui la velocità e le dimensioni sono più importanti. Ma, come puoi vedere con la mia versione 2 della formula. L'uso di LERP per sfumare si tradurrà in piacevoli arance attraverso la gamma di tonalità. Dai un'occhiata alla tabella dei colori qui sotto e fammi sapere se quella gamma di tonalità non è dannatamente vicina alla precisione effettiva.
Pimp Trizkit

Mi sono un po 'confuso con la struttura qui, ma hai ragione, i livelli di arancione per shadeColor1 sembrano essere molto buoni.
Henrik

Lol, intendi shadeColor2. Immagino che la struttura di cui stai parlando sia il layout generale della risposta stessa? Qualche suggerimento per chiarire?
Pimp Trizkit,

3
C'è solo un problema nella funzione con # sopra è che non crea gli zero iniziali se il codice esadecimale finale inizia con zero. Ad esempio, se il codice esadecimale è # 00a6b7, verrà generato come # a6b7, che non funzionerà se utilizzato come css. Puoi correggerlo sostituendo la riga di ritorno con questa: var string = "000000" + (g | (b << 8) | (r << 16)). ToString (16); return (usePound? "#": "") + string.substr (string.length-6);
Rafael Levy,

Risposte:


877

Bene, questa risposta è diventata la sua bestia. Molte nuove versioni, stava diventando stupido a lungo. Mille grazie a tutti coloro che hanno contribuito a questa risposta. Ma, per renderlo semplice per le masse. Ho archiviato tutte le versioni / cronologia dell'evoluzione di questa risposta sul mio github . E ricominciato da capo su StackOverflow qui con la versione più recente. Un ringraziamento speciale va a Mike 'Pomax' Kamermans per questa versione. Mi ha dato la nuova matematica.


Questa funzione ( pSBC) prenderà un colore web HEX o RGB.pSBCpuò sfumare più scuro o più chiaro o mescolarlo con un secondo colore e può anche passarlo attraverso ma convertire da esadecimale a RGB (Hex2RGB) o da RGB a esadecimale (RGB2Hex). Tutto senza di te nemmeno sapere quale formato di colore stai usando.

Funziona molto velocemente, probabilmente il più veloce, soprattutto considerando le sue numerose funzionalità. È passato molto tempo. Vedi tutta la storia sul mio github . Se vuoi il modo assolutamente più piccolo e più veloce per sfumare o sfumare, vedi le Micro funzioni di seguito e usa uno dei demoni della velocità a 2 linee. Sono fantastici per animazioni intense, ma questa versione qui è abbastanza veloce per la maggior parte delle animazioni.

Questa funzione utilizza Log Blending o Linear Blending. Tuttavia, NON converte in HSL per schiarire o scurire correttamente un colore. Pertanto, i risultati di questa funzione differiranno da quelle funzioni molto più grandi e molto più lente che utilizzano HSL.

jsFiddle con pSBC

github> pSBC Wiki

Caratteristiche:

  • Rileva automaticamente e accetta i colori esadecimali standard sotto forma di stringhe. Ad esempio: "#AA6622"o "#bb551144".
  • Rileva automaticamente e accetta i colori RGB standard sotto forma di stringhe. Ad esempio: "rgb(123,45,76)"o"rgba(45,15,74,0.45)" .
  • Sfuma i colori in bianco o nero in percentuale.
  • Unisce i colori insieme in percentuale.
  • Esegue la conversione Hex2RGB e RGB2Hex contemporaneamente o da solo.
  • Accetta codici colore HEX a 3 cifre (o 4 cifre con alpha), nel formato #RGB (o #RGBA). Li espanderà. Ad esempio: "#C41"diventa"#CC4411" .
  • Accetta e (lineare) fonde i canali alfa. Se il c0colore (da) o c1(a) ha un canale alfa, il colore restituito avrà un canale alfa. Se entrambi i colori hanno un canale alfa, il colore restituito sarà una fusione lineare dei due canali alfa utilizzando la percentuale indicata (proprio come se fosse un canale di colore normale). Se solo uno dei due colori ha un canale alfa, questo alfa verrà semplicemente passato al colore restituito. Ciò consente di sfumare / sfumare un colore trasparente mantenendo il livello di trasparenza. Oppure, se anche i livelli di trasparenza dovessero fondersi, assicurati che entrambi i colori abbiano delle alfa. Durante l'ombreggiatura, passerà attraverso il canale alfa direttamente. Se vuoi l'ombreggiatura di base che ombreggia anche il canale alfa, usa rgb(0,0,0,1)o rgb(255,255,255,1)come tuoc1(a) colore (o loro equivalenti esadecimali). Per i colori RGB, il canale alfa del colore restituito verrà arrotondato al terzo decimale.
  • Le conversioni RGB2Hex e Hex2RGB sono implicite quando si utilizza la fusione. Indipendentemente dal c0(da) colore; il colore restituito sarà sempre nel formato colore del c1(a) colore, se presente. Se non c'è nessun c1(a) colore, passa 'c'come c1colore e sfumerà e convertirà qualunque sia il c0colore. Se si desidera solo la conversione, passare anche 0come percentuale ( p). Se il c1colore viene omesso o non stringviene passato, non verrà convertito.
  • Una funzione secondaria viene aggiunta anche al globale. pSBCrpuò essere passato un colore esadecimale o RGB e restituisce un oggetto contenente queste informazioni sul colore. È nella forma: {r: XXX, g: XXX, b: XXX, a: X.XXX}. Where .r, .ge .bhanno un intervallo compreso tra 0 e 255. E quando non è presente l'alfa: .aè -1. Altrimenti: .aha un intervallo compreso tra 0.000 e 1.000.
  • Per l'uscita RGB, emette rgba()sopra rgb()quando un colore con un canale alfa è stato passato in c0(da) e / o c1(a).
  • È stato aggiunto il controllo degli errori minori. Non è perfetto Può ancora bloccarsi o creare jibberish. Ma prenderà alcune cose. Fondamentalmente, se la struttura è in qualche modo sbagliata o se la percentuale non è un numero o non rientra nell'ambito, tornerà null. Un esempio:, pSBC(0.5,"salt") == nulldove come pensa #saltsia un colore valido. Elimina le quattro righe che terminano return null;per rimuovere questa funzione e renderla più veloce e più piccola.
  • Utilizza la fusione dei log. Passare trueper l(il quarto parametro) per utilizzare la fusione lineare.

Codice:

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

Uso:

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

L'immagine seguente aiuterà a mostrare la differenza nei due metodi di fusione:


Micro funzioni

Se vuoi davvero velocità e dimensioni, dovrai usare RGB non HEX. RGB è più diretto e semplice, HEX scrive troppo lentamente ed è disponibile in troppi gusti per un semplice due righe (IE. Potrebbe essere un codice HEX a 3, 4, 6 o 8 cifre). Dovrai anche sacrificare alcune funzionalità, nessun controllo degli errori, nessun HEX2RGB né RGB2HEX. Inoltre, dovrai scegliere una funzione specifica (in base al nome della sua funzione di seguito) per la matematica di fusione dei colori e se vuoi ombreggiature o sfumature. Queste funzioni supportano i canali alfa. E quando entrambi i colori di input hanno gli alfa, li fonderà linearmente. Se solo uno dei due colori ha un alfa, lo passerà direttamente al colore risultante. Di seguito sono due funzioni di rivestimento che sono incredibilmente veloci e piccole:

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

Vuoi maggiori informazioni? Leggi la recensione completa su github .

PT

(Ps Se qualcuno ha la matematica per un altro metodo di fusione, per favore condividi.)


8
Una versione di PHP per chi ne ha bisogno: gist.github.com/chaoszcat/5325115#file-gistfile1-php
Lionel Chan

28
Ho usato TinyColor -tinycolor.darken(color,amount);
FWrnr il

4
Ottimo post ... :) ... ne ho
Matej Ukmar

2
Ecco la versione di PHP per la versione aggiornata di shadeColor2: 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
Mi dispiace, a quanto pare ho perso quel punto. Ci sono forse due ragioni. Il primo e ovvio è che uso Math.Round e non puoi usare numeri decimali per una colorazione accurata (i colori non hanno decimali in esadecimale). Ad esempio, se il canale rosso è 8, aggiungi 10%ottieni 8.8quali round 9. Quindi 9.09%toglie 9e ottieni 8.1819. Il che termina in 8modo che sia un cattivo esempio. Ma illustra ancora che si stanno prendendo 9.09%di 9e non 8.8. Quindi potrebbero esserci dei numeri che non completano esattamente lo stesso del mio esempio qui.
Pimp Trizkit,

122

Ho fatto una soluzione che funziona molto bene per me:

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

Esempio di alleggerimento:

shadeColor("#63C6FF",40);

Esempio Darken:

shadeColor("#63C6FF",-40);

4
Bello, mi piace la percentuale! +1 Tho, potrei fare R = ((R<255)?R:255).toString(16);allora R = R.length==1 ? "0"+R : Rper la velocità. E non sono sicuro del punto di toUpperCase?
Pimp Trizkit,

Non è necessario Lo aggiungo solo per una stampa carina durante il test. Lo modificherò.
Pablo,

Molto bella. Tuttavia, il 100% più leggero non dovrebbe diventare completamente bianco e il 100% scuro sempre nero, indipendentemente dal colore? sembra che -100 renda nero qualsiasi colore, ma 100 (positivo) non lo rende completamente bianco.
Kevin M,

4
non funziona con colori solidi come # ff0000, # 00ff00, # 0000ff
Hitori

Per farlo funzionare con il colore nero ho appena fatto questo trucco 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

Ecco una fodera super semplice basata sulla risposta di 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));
}

Esempi:

adjust('#ffffff', -20) => "#ebebeb"
adjust('000000', 20) => "#141414"

7
"super semplice".
Andrew,

5

Questo è quello che ho usato in base alla tua funzione. Preferisco usare passaggi oltre la percentuale perché è più intuitivo per me.

Ad esempio, il 20% di un valore di 200 blu è molto diverso dal 20% di un valore di 40 blu.

Comunque, ecco la mia modifica, grazie per la tua funzione originale.

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;

}

Ho trovato questo molto più utile della risposta migliore perché la risposta migliore stava rendendo i miei colori molto intensi invece che semplicemente più scuri. Saluti Eric
Worm,

4

Ho provato la tua funzione e c'era un piccolo bug: se un valore 'r' finale è solo di 1 cifra, il risultato si presenta come: 'a0a0a' quando il valore giusto è '0a0a0a', per esempio. L'ho appena risolto aggiungendo questo invece del tuo ritorno:

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;

Forse non è così bello ma fa il lavoro. Ottima funzione, a proposito. Proprio quello di cui avevo bisogno. :)


1
Grazie per il debug e complimenti! Peccato che non sia una risposta all'esistenza o meno di un modo più veloce, che è la mia domanda principale. Come possibilmente uno che usa tutte le conversioni esadecimali e nessuna base. Tuttavia, immagino tu mi abbia detto se avevo il codice corretto (+1). Sfortunatamente, la correzione ha aggiunto un sovraccarico considerevolmente maggiore (ora la tua chiamata a String 6 volte) e un po 'meno KISS. Forse sarebbe più veloce verificare se il numero base10 è 15 o meno, prima della conversione base16. Ma mi piace!
Pimp Trizkit,

4

hai pensato a una conversione rgb> hsl? quindi basta spostare la luminosità su e giù? questo è il modo in cui vorrei andare.

Una rapida occhiata ad alcuni algoritmi mi ha procurato i seguenti siti.

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

MODIFICA il link sopra non è più valido. È possibile visualizzare git hub per l' origine della pagina o l' essenza

In alternativa, un'altra domanda StackOverflow potrebbe essere un buon posto in cui cercare.


Anche se questa non è la scelta giusta per l'OP, la seguente è un'approssimazione del codice che stavo inizialmente suggerendo. (Supponendo che abbiate funzioni di conversione 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;
}

Questo presuppone:

  1. Hai funzioni hslToRgbe rgbToHsl.
  2. Il parametro colorValueè una stringa nel formato #RRGGBB

Anche se stiamo discutendo di CSS, esiste una sintassi per specificare hsl / hsla per IE9 / Chrome / Firefox.


Interessante, ma non dovrei convertire da stringa esadecimale a RGB da hsl? Sembra più complicato. Forse mi manca qualcosa. Ma sto cercando un modo KISS per farlo, il più velocemente possibile (tempo di esecuzione). Mi sento idealmente, se potessi fare tutto in esadecimale sarebbe il più veloce. Ma la soluzione che ho sviluppato qui prevede di andare su rgb per poter aggiungere un importo incrementale.
Pimp Trizkit,

Sì, suppongo che sarebbe più lento, più complicato e se non usi rgb per la conversione hsl in qualsiasi altro posto, probabilmente non sarebbe la soluzione più semplicistica. Sarebbe, tuttavia, più accurato dell'aggiunta ai valori di rgb anche se io stesso non sono una persona di colore. Tutto dipende da quanto vuoi essere preciso, immagino.
James Khoury,

Qual è la perdita di precisione che menzioni? Suppongo che intendi dire che tutti i colori [web] non sono raggiungibili con rgb o qualcosa del genere?
Pimp Trizkit,

Come ho detto, non so molto del colore: wiki Color Theory
James Khoury,

@Pimp Trizkit: è meno preciso perché (e questa è solo la mia teoria ... non sono un esperto di colore) stai spostando ogni canale della stessa quantità, indipendentemente da quanta parte di quel colore doveva iniziare. Io penso che si tradurrebbe in diminuendo la saturazione perché si sta portando i canali più vicini gli uni agli altri (in percentuale). Certo, se trabocca / trabocca è inevitabile comunque.
Matthew Crumley,

2

Versione C # ... nota che sto ricevendo stringhe di colore in questo formato # FF12AE34, e ho bisogno di ritagliare #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
Non ho mai usato C # prima, ma sembra che le ultime tre dichiarazioni di variabili siano strane. An inte due varsper lo stesso tipo di dati.
Pimp Trizkit,

4
La parola chiave var in C # significa che il compilatore deduce il tipo al momento della compilazione. Quindi nell'esempio sopra int e var definiscono una variabile dello stesso tipo - int. Ciò è utile se si dispone di un nome di tipo lungo o se si desidera fare riferimento a un tipo anonimo. È strano perché user1618171 ha mescolato due stili di dichiarazione variabili - probabilmente un errore di battitura.
Daniel James Bryars,

2

Volevo cambiare un colore ad un livello di luminosità specifico - indipendentemente dalla luminosità del colore prima - ecco una semplice funzione JS che sembra funzionare bene, anche se sono sicuro che potrebbe essere più breve

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! Presumo che pabbia portata 0-100? Non so nemmeno come definire correttamente la luminosità in RGB, questa è una cosa HSL. Ad esempio, è #FF00FFpiù luminoso di #FF0000? In tal caso, ciò implicherebbe che il magenta è due volte più luminoso del rosso. Pertanto, viene utilizzato il test rosso puro. Passa in rosso puro #FF0000, impostato al 50% di luminosità, e qui otteniamo #FF4040, giusto? Avrei indovinato a rendere il rosso del 50% di luminosità, diventeremmo più scuri, visto che è già completamente luminoso .. come in #800000o il 150% di luminosità sarebbe #FF8080. Il rosa è un rosso più luminoso? o il rosso è già completamente luminoso?
Pimp Trizkit

Hai ragione - avrei dovuto menzionare che p deve essere compreso nell'intervallo 1-100!
Torbjörn Josefsson

# FF00FF ha 255 come valore nel canale rosso, 0 nel canale verde e 255 nel canale blu. Più alti sono i valori combinati nei canali, maggiore è la luminosità del colore. Il numero p afferma che vogliamo che il nuovo colore sia luminoso al 50% quanto può essere il colore originale. Non sono al 100% che # FF4040 sia la risposta corretta al "50% più luminoso possibile rosso". La produzione di tonalità più scure (con, in questo caso, un valore inferiore nel canale rosso) richiederebbe una modifica
Torbjörn Josefsson

Sì, stavo solo sottolineando l'ambiguità nel parlare di luminosità in RGB. Se convertito in HSL, il Lcanale è letteralmente luminosità. Il mio problema [personale mentale] qui è che, per me, #FF0000è completamente brillante. Ed #FF4040è più leggero ma non più luminoso .... per me più leggero significa più vicino al bianco, come il rosa. E la luminosità è quanto ha, e ha il rosso pieno, quindi il rosso, è completamente luminoso. Pertanto, #FF0000non può essere reso più luminoso .. ma piuttosto .. più leggero ... forse sono solo un mostro, lol !! Davvero non conosco la teoria dei colori, quindi, sto solo parlando del mio ...
Pimp Trizkit

Ma so che quando cambio la luminosità sul mio monitor, i rossi non diventano rosa ... per me. Quindi è probabilmente qui che ho iniziato la mia logica.
Pimp Trizkit

1

Il seguente metodo ti permetterà di schiarire o scurire il valore di esposizione di una stringa di colore esadecimale (esadecimale):

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

}

Per l'ultimo valore del parametro in GetHexFromRGB (), passa un valore doppio tra -1 e 1 (-1 è nero, 0 è invariato, 1 è bianco):

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

Come semplice tonalità di colore in 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

Questa è una versione php della risposta di Pablo. Sfortunatamente, è più lungo e più lento della soluzione finale e non schiarisce i colori con precisione. Li oscura con precisione anche se. Test con rosso puro (# FF0000), dovrebbe essere un fulmine del 25% (# FF4040). Scopri la fine della mia risposta per la versione PHP di Kevin M della soluzione finale v2.
Pimp Trizkit,

0

Ho creato una porta dell'eccellente libreria xcolor per rimuovere la sua dipendenza jQuery. Ci sono un sacco di funzioni tra cui schiarire e scurire i colori.

In realtà, la conversione di hex in RGB è una funzione completamente separata dai colori più chiari o più scuri. Tieni le cose ASCIUTTE per favore. In ogni caso, una volta che hai un colore RGB, puoi semplicemente aggiungere la differenza tra il livello di luce desiderato e il livello di luce che hai a ciascuno dei valori 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)
}

Vedi https://github.com/fresheneesz/xolor per ulteriori informazioni sulla fonte.


Non ho ancora analizzato il codice per quanto riguarda il mio PO (velocità / dimensioni / precisione). Ma a prima vista ci sono alcuni commenti da fare: 1) Sono d'accordo che la conversione di hex in RGB può essere vista come una funzione completamente separata .. SE il mio problema era destinato a essere risolto con una funzione dry, che non era un requisito. L'intenzione qui era quella di avere una risposta (vedi la mia versione 2) che fosse super veloce e super minuscola (2 righe!) E una che schiarisse e oscurasse un colore esadecimale ... in particolare ... come un autonomo autonomo funzione. In modo che, nel suo utilizzo finale, sarà una semplice chiamata a singola funzione. <
Cont

2) E il caso della Versione 3, a grande richiesta, è l'intenzione di avere una funzione universale autonoma completamente autonoma, il più veloce e il più piccolo possibile, che possa prendere alla cieca un colore esadecimale o RGB e in tutto il loro variazioni. Pertanto, è necessaria una conversione da esadecimale a RGB. <cont.>
Pimp Trizkit,

3) Dopo una semplice analisi del codice. Sembra che funzionerebbe molto più lentamente ed è ovviamente molto più grande della mia versione 2 (che è la vera risposta al mio OP; la versione 3 era per le masse). Per essere onesti, dovrei confrontare questo codice con la mia versione RGB 2 che non fa una conversione e sembra rispondere al tuo punto sulla siccità. E a dire il vero, la tua porta non è molto più semplice da capire rispetto al mio 2 liner per hex. Quindi, mentre è più asciutto, in realtà non è molto più semplice. (la secchezza non ha aiutato molto per la capacità di comprensione) <cont.>
Pimp Trizkit

4) My RGB Versione 2 è una funzione di linea senza conversione 2 se lo si desidera. La mia soluzione particolare per il mio OP originale voleva un esagono. Ecco perché ci sono due diversi tipi di versione 2. Ma menzioni il punto sulla secchezza e le conversioni esadecimali, quindi ora ci stiamo concentrando davvero sulla versione 3. La versione 3 è arrivata molto più tardi; solo dopo la versione 2 era popolare. <cont.>
Pimp Trizkit

5) Mentre concordo sul fatto che l'aridità generalmente aiuta l'universalità. E nella maggior parte dei casi, per capacità di comprensione. Purtroppo è un costo in questo esempio. Questi costi sono che è molto più grande e apparentemente molto più lento e apparentemente utilizza più memoria sia nello stack (con la sua natura ricorsiva) sia in quello globale (2 funzioni; rispetto alla v2).
Pimp Trizkit,

0

Ho desiderato da tempo poter produrre tinte / sfumature di colori, ecco la mia soluzione 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);
    }
};

Alcuni esempi di utilizzo potrebbero essere d'aiuto. E forse qualche spiegazione sul perché questa versione rispetto alle altre. Questa versione sembra funzionare molto più lentamente. Ed è molto più lungo. E non sembra ombreggiare accuratamente. Sembra che tu stia usando LERP o qualcosa di simile ... che è buono. Sfortunatamente, è solo da un canale, quindi lo stesso valore viene utilizzato su tutti i canali. Questo non è giusto, al fine di ottenere una maggiore precisione, dovresti LERP ogni canale individualmente. Come fa la mia risposta a questa domanda. Inoltre è più piccolo e più veloce e controlla gli errori e gestisce rgb, e fa conversioni, potrei continuare
Pimp Trizkit

Un esempio di utilizzo: varianteHue ("6e124c", 77) dove il primo argomento è il colore in esadecimale e il secondo la variazione percentuale. Una variazione percentuale positiva oscura (scurisce) mentre un valore negativo tinge (schiarisce) il risultato. Ho scritto la routine come il mio primo tentativo poche ore prima di trovare questa pagina e pubblicarla semplicemente come una questione di interesse. Non sapevo che dovevo migliorare i tuoi sforzi o che avevo richiesto la tua approvazione prima di farlo. È interamente opera mia senza riferimento a nessun altro. Non ho sentito parlare di LERP Lo controllerò, grazie per il suggerimento.
user2655360

Hehe, beh, certo che non devi fare niente! E tutti ti ringraziamo per i tuoi sforzi! Le mie prime preoccupazioni principali sono state quelle elencate per prime. Cercando di aiutarti con la tua risposta in modo che possa ottenere voti. (mostra gli usi e la spiegazione di come funziona, ecc.) Le altre cose sono ovviamente una rapida analisi per aiutare ulteriormente la conoscenza di tutti . Scusa, se mi è sembrato un po 'aggressivo. Ma un altro suggerimento è quello di farlo accettare i #colori esadecimali. Scusate se mi è sembrato .. "approvazione" ... L'ho visto come una recensione tra pari. Se non vuoi che qualcuno analizzi il tuo codice o offra feedback, mi scuso.
Pimp Trizkit,

0

Il tuo approccio è ok :) Semplifico un po 'la tua versione più breve (per il controllo della saturazione guarda qui )

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

E versione con # e controllo delle gamme di colori


0

Ho anche creato un pacchetto semplice. Ho usato la convessità di IR ^ 3, ovviamente, i valori RGB sono in IN ^ 3 che non è convesso, quindi non è davvero perfetto

Per scurire ho usato quanto segue

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

E per alleggerire

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

Ecco il pacchetto https://github.com/MarchWorks/colortone

Demo https://colortone.now.sh/

con il modo in cui sto facendo le cose se passi un rapporto di -1 finirai con nero, bianco se il rapporto è 1. Passando 0 poiché il rapporto non cambierà il colore


0

Ne avevo bisogno in C #, potrebbe aiutare gli sviluppatori .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;
    }

Hai convertito la funzione "non funzionante" che non si occupa degli zeri iniziali e puoi andare al di sopra di FF nelle somme, = modifica la componente del colore sopra ...
B. Vai

Quando ho provato la funzione non ha funzionato a causa di valori non esadecimali (16 = 0xF) e (8 = 0x8) e dà un colore in 8 posizioni, ma ora funziona molto bene
Nassim

hai provato ad aumentare da FF? (diciamo da 0xFFFFFF + 0x000004): il tuo codice passa sopra quel valore massimo (diciamo a 0x1000003), invece di non aumentare, e soprattutto impostando i 2 componenti di colore superiori su 00 che è il cambiamento più grande che potrebbero fare ...
B. Vai

hai ragione, grazie per l'osservazione, tranne un input di limiti intorno a fff e 000 funzionerà benissimo.
Nassim,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.