709 Stimmen

Programmatisch eine Hex-Farbe aufhellen oder abdunkeln (oder rgb, und Farben mischen)

Hier ist eine Funktion, an der ich gearbeitet habe, um eine hexadezimale Farbe programmatisch um einen bestimmten Betrag aufzuhellen oder abzudunkeln. Geben Sie einfach eine Zeichenkette ein wie "3F6D2A" für die Farbe ( col ) und eine ganze Zahl zur Basis 10 ( amt ) für die Menge, die aufgehellt oder verdunkelt werden soll. Um abzudunkeln, geben Sie eine negative Zahl ein (d. h. -20 ).

Der Grund dafür war, dass alle Lösungen, die ich bisher gefunden habe, das Problem zu kompliziert zu machen schienen. Und ich hatte das Gefühl, dass es mit nur ein paar Zeilen Code getan werden könnte. Bitte lassen Sie mich wissen, wenn Sie irgendwelche Probleme finden oder Anpassungen vornehmen möchten, die das Ganze beschleunigen würden.

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

Für den Einsatz in der Entwicklung gibt es hier eine leichter zu lesende Version:

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

Und schließlich eine Version zur Behandlung von Farben, die das "#" am Anfang haben (oder auch nicht). Und eine Anpassung für unpassende Farbwerte:

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, es sind also nicht nur ein paar Zeilen, aber es scheint viel einfacher zu sein, und wenn Sie das "#" nicht verwenden und nicht auf Farben außerhalb des Bereichs prüfen müssen, sind es auch nur ein paar Zeilen.

Wenn Sie das "#" nicht verwenden, können Sie es einfach in den Code einfügen:

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

Meine Hauptfrage ist wohl, ob ich hier richtig liege. Umfasst dies nicht auch einige (normale) Situationen?

1063voto

Pimp Trizkit Punkte 17906

Nun, diese Antwort hat sich zu einer eigenen Angelegenheit entwickelt. Viele neue Versionen, es wurde einfach zu lang. Vielen Dank an all die vielen, vielen Mitwirkenden an dieser Antwort. Aber, um es für die Masse einfach zu halten. Ich habe alle Versionen/Geschichte der Entwicklung dieser Antwort auf meiner Website archiviert github . Und habe es hier auf StackOverflow mit der neuesten Version neu gestartet. Ein besonderer Dank geht an Mike 'Pomax' Kamermans für diese Version. Er gab mir die neue Mathematik.


Diese Funktion ( pSBC ) nimmt eine HEX- oder RGB-Webfarbe an. pSBC kann sie dunkler oder heller schattieren oder mit einer zweiten Farbe mischen und kann sie auch direkt durchlaufen, aber von Hex nach RGB (Hex2RGB) oder RGB nach Hex (RGB2Hex) konvertieren. Und das alles, ohne dass Sie überhaupt wissen, welches Farbformat Sie verwenden.

Es läuft wirklich schnell, wahrscheinlich am schnellsten, vor allem wenn man die vielen Funktionen bedenkt. Es war eine lange Zeit in der Entwicklung. Siehe die ganze Geschichte auf meiner github . Wenn Sie die absolut kleinstmögliche und schnellste Art des Schattierens oder Verblendens wünschen, sehen Sie sich die Mikrofunktionen unten an und verwenden Sie einen der 2-Liner Speed Demons. Sie sind großartig für intensive Animationen, aber diese Version hier ist schnell genug für die meisten Animationen.

Diese Funktion verwendet Log Blending oder Linear Blending. Sie wandelt jedoch NICHT in HSL um, um eine Farbe richtig aufzuhellen oder abzudunkeln. Deshalb, Die Ergebnisse dieser Funktion werden sich unterscheiden von den viel größeren und viel langsameren Funktionen, die HSL verwenden.

jsFiddle mit pSBC

github > pSBC-Wiki

Merkmale:

  • Automatische Erkennung und Annahme von Standard-Hex-Farben in Form von Zeichenketten. Zum Beispiel: "#AA6622" o "#bb551144" .
  • Automatische Erkennung und Annahme von Standard-RGB-Farben in Form von Zeichenketten. Zum Beispiel: "rgb(123,45,76)" o "rgba(45,15,74,0.45)" .
  • Schattiert Farben prozentual zu Weiß oder Schwarz.
  • Mischt Farben prozentual zusammen.
  • Konvertiert Hex2RGB und RGB2Hex gleichzeitig oder alleine.
  • Akzeptiert 3-stellige (oder 4-stellige mit Alpha) HEX-Farbcodes in der Form #RGB (oder #RGBA). Es wird sie expandieren. Zum Beispiel: "#C41" wird "#CC4411" .
  • Akzeptiert und überblendet (linear) Alphakanäle. Wenn entweder die c0 (von) Farbe oder die c1 (bis) Farbe einen Alphakanal hat, dann hat die zurückgegebene Farbe einen Alphakanal. Wenn beide Farben einen Alphakanal haben, wird die zurückgegebene Farbe eine lineare Mischung der beiden Alphakanäle mit dem angegebenen Prozentsatz sein (so als ob es ein normaler Farbkanal wäre). Wenn nur eine der beiden Farben einen Alphakanal hat, wird dieser Alphakanal einfach an die zurückgegebene Farbe durchgereicht. Auf diese Weise kann eine transparente Farbe überblendet/abgeschattet werden, während die Transparenzstufe beibehalten wird. Oder, wenn die Transparenzstufen ebenfalls überblendet werden sollen, stellen Sie sicher, dass beide Farben Alphakanäle haben. Beim Schattieren wird der Alphakanal direkt durchgereicht. Wenn Sie eine einfache Schattierung wünschen, die auch den Alphakanal schattiert, dann verwenden Sie rgb(0,0,0,1) o rgb(255,255,255,1) als Ihr c1 (bis) Farbe (oder ihre Hex-Äquivalente). Bei RGB-Farben wird der Alphakanal der zurückgegebenen Farbe auf 3 Dezimalstellen gerundet.
  • RGB2Hex- und Hex2RGB-Konvertierungen sind bei der Verwendung von Blending implizit. Unabhängig von der c0 (von) Farbe; die zurückgegebene Farbe entspricht immer dem Farbformat der c1 (zu) Farbe, wenn es eine gibt. Wenn es keine gibt c1 (zu) Farbe, dann passieren 'c' in als die c1 Farbe und schattiert und konvertiert, was immer die c0 Farbe ist. Wenn nur eine Konvertierung gewünscht ist, dann übergeben Sie 0 als Prozentsatz eingeben ( p ) sowie. Wenn die c1 Farbe weggelassen wird oder eine nicht string übergeben wird, wird es nicht konvertiert.
  • Außerdem wird dem Global eine sekundäre Funktion hinzugefügt. pSBCr kann eine Hex- oder RGB-Farbe übergeben werden und gibt ein Objekt zurück, das diese Farbinformationen enthält. Es hat die Form: {r: XXX, g: XXX, b: XXX, a: X.XXX}. Wobei .r , .g y .b haben einen Bereich von 0 bis 255. Und wenn es kein Alpha gibt: .a ist -1. Andernfalls: .a hat einen Bereich von 0,000 bis 1,000.
  • Für die RGB-Ausgabe gibt er Folgendes aus rgba() sur rgb() wenn eine Farbe mit einem Alphakanal in c0 (von) und/oder c1 (zu).
  • Eine kleine Fehlerprüfung wurde hinzugefügt. Sie ist nicht perfekt. Sie kann immer noch abstürzen oder Kauderwelsch erzeugen. Aber sie fängt einige Dinge ab. Wenn die Struktur in irgendeiner Weise falsch ist oder wenn der Prozentsatz keine Zahl ist oder außerhalb des Gültigkeitsbereichs liegt, wird grundsätzlich Folgendes zurückgegeben null . Ein Beispiel: pSBC(0.5,"salt") == null während sie denkt #salt ist eine gültige Farbe. Löschen Sie die vier Zeilen, die mit return null; um diese Funktion zu entfernen und sie schneller und kleiner zu machen.
  • Verwendet Log Blending. Durchlauf true für l (der 4. Parameter), um die lineare Überblendung zu verwenden.

Code:

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

Verwendung:

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

Die nachstehende Abbildung verdeutlicht den Unterschied zwischen den beiden Mischmethoden:


Mikrofunktionen

Wenn Sie wirklich Geschwindigkeit und Größe wollen, müssen Sie RGB und nicht HEX verwenden. RGB ist unkomplizierter und einfacher, HEX schreibt zu langsam und hat zu viele Varianten für einen einfachen Zweizeiler (z. B. könnte es ein 3-, 4-, 6- oder 8-stelliger HEX-Code sein). Außerdem müssen Sie auf einige Funktionen verzichten: keine Fehlerprüfung, kein HEX2RGB oder RGB2HEX. Außerdem müssen Sie eine bestimmte Funktion (basierend auf dem unten stehenden Funktionsnamen) für die Berechnung der Farbmischung wählen und angeben, ob Sie eine Schattierung oder eine Überblendung wünschen. Diese Funktionen unterstützen Alphakanäle. Wenn beide Eingabefarben Alphakanäle haben, werden sie linear gemischt. Wenn nur eine der beiden Farben einen Alphakanal hat, wird dieser direkt an die resultierende Farbe weitergegeben. Im Folgenden finden Sie zwei Linienfunktionen, die unglaublich schnell und klein sind:

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

Möchten Sie mehr Informationen? Lesen Sie den vollständigen Bericht auf github .

PT

(P.s. Wenn jemand die Mathematik für eine andere Mischmethode kennt, bitte mitteilen).

249voto

Pablo Punkte 2220

Ich habe eine Lösung gefunden, die für mich sehr gut funktioniert:

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

Beispiel Aufhellen:

shadeColor("#63C6FF",40);

Beispiel Verdunkeln:

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

115voto

supersan Punkte 5005

Hier ist ein super einfacher Einzeiler, der auf Erics Antwort basiert

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"

58voto

antoni Punkte 4235

Ich füge hier meine 2 Cents hinzu, eine zufriedenstellende kleine Kombination verschiedener Antworten:

const colorShade = (col, amt) => {
  col = col.replace(/^#/, '')
  if (col.length === 3) col = col[0] + col[0] + col[1] + col[1] + col[2] + col[2]

  let [r, g, b] = col.match(/.{2}/g);
  ([r, g, b] = [parseInt(r, 16) + amt, parseInt(g, 16) + amt, parseInt(b, 16) + amt])

  r = Math.max(Math.min(255, r), 0).toString(16)
  g = Math.max(Math.min(255, g), 0).toString(16)
  b = Math.max(Math.min(255, b), 0).toString(16)

  const rr = (r.length < 2 ? '0' : '') + r
  const gg = (g.length < 2 ? '0' : '') + g
  const bb = (b.length < 2 ? '0' : '') + b

  return `#${rr}${gg}${bb}`
}

nimmt eine Farbe an, die mit # oder nicht, mit 6 Zeichen oder 3 Zeichen.

Beispiel für die Verwendung: colorShade('#54b946', -40)

Hier ist die Ausgabe von 4 Farben mit jeweils 3 helleren und 3 dunkleren Farbtönen (der Betrag ist hier ein Vielfaches von 40).

enter image description here

11voto

Eric Sloan Punkte 149

Dies habe ich auf der Grundlage Ihrer Funktion verwendet. Ich verwende lieber Schritte als Prozente, weil es für mich intuitiver ist.

So sind beispielsweise 20 % eines 200er Blauwerts etwas ganz anderes als 20 % eines 40er Blauwerts.

Wie auch immer, hier ist meine Änderung, danke für Ihre ursprüngliche Funktion.

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;

}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X