Farbkonvertierung in JavaScript

Avatar of Jon Kantner
Jon Kantner am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Eine Herausforderung, die ich beim Erstellen eines Bild-„Emotifier“ hatte, bestand darin, die Farbräume von Werten, die mit getImageData() erhalten wurden, von RGB in HSL zu ändern. Ich verwendete Arrays von Emojis, die nach Helligkeit und Sättigung sortiert waren, und diese basierten auf HSL für die besten Übereinstimmungen der durchschnittlichen Pixelfarben mit den Emojis.

In diesem Artikel werden wir Funktionen untersuchen, die für die Konvertierung von sowohl opak als auch mit Alpha aktivierten Farbwerten nützlich sind. Moderne Browser unterstützen derzeit die Farbräume RGB(A), Hex und HSL(A). Die Funktionen und Notationen dafür sind rgb(), rgba(), #rgb/#rrggbb, #rgba/#rrggbbaa, hsl() und hsla(). Browser haben auch immer integrierte Namen wie aliceblue unterstützt.

Balls with color values being inserted into a machine and coming out as HSL

Dabei werden wir die Verwendung einiger Farbsyntaxes aus einer neuen Level 4 des CSS Colors Moduls kennenlernen. Zum Beispiel haben wir jetzt Hex mit Alpha, wie wir erwähnt haben (#rgba/#rrggbbaa), und RGB- und HSL-Syntaxen benötigen keine Kommas mehr (Werte wie rgb(255 0 0) und hsl(240 100% 50%) wurden legal!).

Die Browserunterstützung für CSS Colors Level 4 ist zum Zeitpunkt des Schreibens noch nicht universell, daher sollten Sie in CSS keine neuen Farbsyntaxen in Microsoft-Browsern oder Safari erwarten.

RGB zu Hex

Die Konvertierung von RGB zu Hex ist lediglich eine Änderung der Basis. Wir konvertieren die Rot-, Grün- und Blauwerte von Dezimal nach Hexadezimal mit toString(16). Nach dem Voranstellen von 0en für einzelne Ziffern und darunter können wir sie und # zu einer einzigen return-Anweisung verketten.

function RGBToHex(r,g,b) {
  r = r.toString(16);
  g = g.toString(16);
  b = b.toString(16);

  if (r.length == 1)
    r = "0" + r;
  if (g.length == 1)
    g = "0" + g;
  if (b.length == 1)
    b = "0" + b;

  return "#" + r + g + b;
}

Hex aus RGB im String

Alternativ können wir ein einzelnes String-Argument mit Rot, Grün und Blau, getrennt durch Kommas oder Leerzeichen (z. B. "rgb(255,25,2)", "rgb(255 25 2)"), verwenden. Substring, um rgb( zu eliminieren, teile den Rest nach ), dann teile das erste Element des Ergebnisses nach dem jeweiligen Trennzeichen (sep). r, g und b werden nun zu lokalen Variablen. Dann verwenden wir + vor den geteilten Strings, um sie zurück in Zahlen zu konvertieren, bevor wir die Hex-Werte erhalten.

function RGBToHex(rgb) {
  // Choose correct separator
  let sep = rgb.indexOf(",") > -1 ? "," : " ";
  // Turn "rgb(r,g,b)" into [r,g,b]
  rgb = rgb.substr(4).split(")")[0].split(sep);

  let r = (+rgb[0]).toString(16),
      g = (+rgb[1]).toString(16),
      b = (+rgb[2]).toString(16);

  if (r.length == 1)
    r = "0" + r;
  if (g.length == 1)
    g = "0" + g;
  if (b.length == 1)
    b = "0" + b;

  return "#" + r + g + b;
}

Zusätzlich können wir Strings mit Kanalwerten als Prozente zulassen, indem wir die Schleife nach der Neudefinition von rgb hinzufügen. Sie entfernt die %s und wandelt den Rest in Werte von 255 um.

function RGBToHex(rgb) {
  let sep = rgb.indexOf(",") > -1 ? "," : " ";
  rgb = rgb.substr(4).split(")")[0].split(sep);

  // Convert %s to 0–255
  for (let R in rgb) {
    let r = rgb[R];
    if (r.indexOf("%") > -1)
      rgb[R] = Math.round(r.substr(0,r.length - 1) / 100 * 255);
      /* Example:
      75% -> 191
      75/100 = 0.75, * 255 = 191.25 -> 191
      */
  }

  ...
}

Jetzt können wir Werte wie diese angeben

  • rgb(255,25,2)
  • rgb(255 25 2)
  • rgb(50%,30%,10%)
  • rgb(50% 30% 10%)

RGBA zu Hex (#rrggbbaa)

Die Konvertierung von RGBA zu Hex mit der #rgba- oder #rrggbbaa-Notation folgt praktisch demselben Prozess wie das opake Gegenstück. Da Alpha (a) normalerweise ein Wert zwischen 0 und 1 ist, müssen wir ihn mit 255 multiplizieren, das Ergebnis runden und dann in Hexadezimal konvertieren.

function RGBAToHexA(r,g,b,a) {
  r = r.toString(16);
  g = g.toString(16);
  b = b.toString(16);
  a = Math.round(a * 255).toString(16);

  if (r.length == 1)
    r = "0" + r;
  if (g.length == 1)
    g = "0" + g;
  if (b.length == 1)
    b = "0" + b;
  if (a.length == 1)
    a = "0" + a;

  return "#" + r + g + b + a;
}

Um dies mit einem einzigen String zu tun (einschließlich mit Prozentangaben), können wir dem folgen, was wir zuvor getan haben. Beachten Sie auch den zusätzlichen Schritt des Herausschneidens eines Schrägstrichs. Da CSS Colors Level 4 die Syntax von rgba(r g b / a) unterstützt, erlauben wir diese hier. Alpha-Werte können jetzt Prozente sein! Dies entfernt die Fesseln, die wir früher nur von 0-1 hatten. Daher wird die for-Schleife, die durch rgba läuft, einen Teil enthalten, um das % vom Alpha zu entfernen, ohne mit 255 zu multiplizieren (wenn R 3 für Alpha ist). Bald können wir Werte wie rgba(255 128 0 / 0.8) und rgba(100% 21% 100% / 30%) verwenden!

function RGBAToHexA(rgba) {
  let sep = rgba.indexOf(",") > -1 ? "," : " "; 
  rgba = rgba.substr(5).split(")")[0].split(sep);
                
  // Strip the slash if using space-separated syntax
  if (rgba.indexOf("/") > -1)
    rgba.splice(3,1);

  for (let R in rgba) {
    let r = rgba[R];
    if (r.indexOf("%") > -1) {
      let p = r.substr(0,r.length - 1) / 100;

      if (R < 3) {
        rgba[R] = Math.round(p * 255);
      } else {
        rgba[R] = p;
      }
    }
  }
}

Dann passen wir, wo die Kanäle in Hex konvertiert werden, a an, um ein Element von rgba[] zu verwenden.

function RGBAToHexA(rgba) {
  ...
    
  let r = (+rgba[0]).toString(16),
      g = (+rgba[1]).toString(16),
      b = (+rgba[2]).toString(16),
      a = Math.round(+rgba[3] * 255).toString(16);

  if (r.length == 1)
    r = "0" + r;
  if (g.length == 1)
    g = "0" + g;
  if (b.length == 1)
    b = "0" + b;
  if (a.length == 1)
    a = "0" + a;

  return "#" + r + g + b + a;
}

Jetzt unterstützt die Funktion Folgendes

  • rgba(255,25,2,0.5)
  • rgba(255 25 2 / 0.5)
  • rgba(50%,30%,10%,0.5)
  • rgba(50%,30%,10%,50%)
  • rgba(50% 30% 10% / 0.5)
  • rgba(50% 30% 10% / 50%)

Hex zu RGB

Wir wissen, dass die Länge von Hex-Werten entweder 3 oder 6 sein muss (plus #). In beiden Fällen beginnen wir jeden Rot- (r), Grün- (g) und Blau- (b) Wert mit "0x", um sie in Hexadezimal zu konvertieren. Wenn wir einen 3-stelligen Wert angeben, verketten wir denselben Wert zweimal für jeden Kanal. Wenn es ein 6-stelliger Wert ist, verketten wir die ersten beiden für Rot, die nächsten beiden für Grün und die letzten beiden für Blau. Um die Werte für den endgültigen rgb()-String zu erhalten, stellen wir den Variablen + voran, um sie von Strings zurück in Zahlen zu konvertieren, was uns die benötigten Dezimalwerte liefert.

function hexToRGB(h) {
  let r = 0, g = 0, b = 0;

  // 3 digits
  if (h.length == 4) {
    r = "0x" + h[1] + h[1];
    g = "0x" + h[2] + h[2];
    b = "0x" + h[3] + h[3];

  // 6 digits
  } else if (h.length == 7) {
    r = "0x" + h[1] + h[2];
    g = "0x" + h[3] + h[4];
    b = "0x" + h[5] + h[6];
  }
  
  return "rgb("+ +r + "," + +g + "," + +b + ")";
}

RGB mit %s aus Hex ausgeben

Wenn wir rgb() mit Prozente zurückgeben wollen, können wir die Funktion so modifizieren, dass sie einen optionalen isPct-Parameter verwendet

function hexToRGB(h,isPct) {
  let r = 0, g = 0, b = 0;
  isPct = isPct === true;

  if (h.length == 4) {
    r = "0x" + h[1] + h[1];
    g = "0x" + h[2] + h[2];
    b = "0x" + h[3] + h[3];
    
  } else if (h.length == 7) {
    r = "0x" + h[1] + h[2];
    g = "0x" + h[3] + h[4];
    b = "0x" + h[5] + h[6];
  }
    
  if (isPct) {
    r = +(r / 255 * 100).toFixed(1);
    g = +(g / 255 * 100).toFixed(1);
    b = +(b / 255 * 100).toFixed(1);
  }
  
  return "rgb(" + (isPct ? r + "%," + g + "%," + b + "%" : +r + "," + +g + "," + +b) + ")";
}

Unter der letzten if-Anweisung konvertieren die Verwendung von +s r, g und b in Zahlen. Jedes toFixed(1) mit ihnen rundet das Ergebnis auf die nächste Zehntelstelle. Zusätzlich werden wir keine ganzen Zahlen mit .0 oder den jahrzehntealten Quark erhalten, der Zahlen wie 0.30000000000000004 erzeugt. Daher haben wir im return die +s direkt vor dem ersten r, g und b weggelassen, um NaNs zu vermeiden, die durch die %s verursacht werden. Jetzt können wir hexToRGB("#ff0",true) verwenden, um rgb(100%,100%,0%) zu erhalten!

Hex (#rrggbbaa) zu RGBA

Das Verfahren für Hex-Werte mit Alpha sollte wieder ähnlich dem letzten sein. Wir erkennen einfach einen 4- oder 8-stelligen Wert (plus #) und konvertieren dann das Alpha und teilen es durch 255. Um präzisere Ergebnisse zu erhalten, aber keine langen Dezimalzahlen für Alpha, können wir toFixed(3) verwenden.

function hexAToRGBA(h) {
  let r = 0, g = 0, b = 0, a = 1;

  if (h.length == 5) {
    r = "0x" + h[1] + h[1];
    g = "0x" + h[2] + h[2];
    b = "0x" + h[3] + h[3];
    a = "0x" + h[4] + h[4];

  } else if (h.length == 9) {
    r = "0x" + h[1] + h[2];
    g = "0x" + h[3] + h[4];
    b = "0x" + h[5] + h[6];
    a = "0x" + h[7] + h[8];
  }
  a = +(a / 255).toFixed(3);

  return "rgba(" + +r + "," + +g + "," + +b + "," + a + ")";
}

RGBA mit %s aus Hex ausgeben

Für eine Version, die Prozente ausgibt, können wir das tun, was wir in hexToRGB() getan haben – r, g und b auf 0–100 % umstellen, wenn isPct true ist.

function hexAToRGBA(h,isPct) {
  let r = 0, g = 0, b = 0, a = 1;
  isPct = isPct === true;
    
  // Handling of digits
  ...

  if (isPct) {
    r = +(r / 255 * 100).toFixed(1);
    g = +(g / 255 * 100).toFixed(1);
    b = +(b / 255 * 100).toFixed(1);
  }
  a = +(a / 255).toFixed(3);

  return "rgba(" + (isPct ? r + "%," + g + "%," + b + "%," + a : +r + "," + +g + "," + +b + "," + a) + ")";
}

Hier ist eine schnelle Lösung, wenn das Alpha ebenfalls ein Prozent sein soll: Verschieben Sie die Anweisung, wo a neu definiert wird, über die letzte if-Anweisung. Ändern Sie dann in dieser Anweisung a so, dass es wie r, g und b ist. Wenn isPct true ist, muss a auch das % erhalten.

function hexAToRGBA(h,isPct) {
  ...
    
  a = +(a / 255).toFixed(3);
  if (isPct) {
    r = +(r / 255 * 100).toFixed(1);
    g = +(g / 255 * 100).toFixed(1);
    b = +(b / 255 * 100).toFixed(1);
    a = +(a * 100).toFixed(1);
  }

  return "rgba(" + (isPct ? r + "%," + g + "%," + b + "%," + a + "%" : +r + "," + +g + "," + +b + "," + a) + ")";
}

Wenn wir jetzt #7f7fff80 eingeben, sollten wir rgba(127,127,255,0.502) oder rgba(49,8%,49,8%,100%,50,2%) erhalten.

RGB zu HSL

Das Erhalten von HSL-Werten aus RGB oder Hex ist etwas anspruchsvoller, da eine größere Formel dahinter steckt. Zuerst müssen wir Rot, Grün und Blau durch 255 teilen, um Werte zwischen 0 und 1 zu verwenden. Dann ermitteln wir das Minimum und Maximum dieser Werte (cmin und cmax) sowie die Differenz zwischen ihnen (delta). Wir benötigen dieses Ergebnis als Teil der Berechnung von Farbton und Sättigung. Direkt nach dem delta initialisieren wir den Farbton (h), die Sättigung (s) und die Helligkeit (l).

function RGBToHSL(r,g,b) {
  // Make r, g, and b fractions of 1
  r /= 255;
  g /= 255;
  b /= 255;

  // Find greatest and smallest channel values
  let cmin = Math.min(r,g,b),
      cmax = Math.max(r,g,b),
      delta = cmax - cmin,
      h = 0,
      s = 0,
      l = 0;
}

Als Nächstes müssen wir den Farbton berechnen, der durch den größten Kanalwert in cmax (oder wenn alle Kanäle gleich sind) bestimmt wird. Wenn es keine Differenz zwischen den Kanälen gibt, beträgt der Farbton 0. Wenn cmax Rot ist, dann ist die Formel ((g - b) / delta) % 6. Wenn Grün, dann (b - r) / delta + 2. Dann, wenn Blau, (r - g) / delta + 4. Schließlich multiplizieren wir das Ergebnis mit 60 (um den Gradwert zu erhalten) und runden es. Da Farbtöne nicht negativ sein sollten, addieren wir gegebenenfalls 360 dazu.

function RGBToHSL(r,g,b) {
  ...
  // Calculate hue
  // No difference
  if (delta == 0)
    h = 0;
  // Red is max
  else if (cmax == r)
    h = ((g - b) / delta) % 6;
  // Green is max
  else if (cmax == g)
    h = (b - r) / delta + 2;
  // Blue is max
  else
    h = (r - g) / delta + 4;

  h = Math.round(h * 60);
    
  // Make negative hues positive behind 360°
  if (h < 0)
      h += 360;
}

Alles Übrige sind Sättigung und Helligkeit. Berechnen wir die Helligkeit, bevor wir die Sättigung berechnen, da die Sättigung davon abhängt. Sie ist die Summe des Maximums und Minimums der Kanalwerte, halbiert ((cmax + cmin) / 2). Dann bestimmt delta, wie die Sättigung sein wird. Wenn sie 0 ist (keine Differenz zwischen cmax und cmin), ist die Sättigung automatisch 0. Andernfalls ist sie 1 minus dem Absolutwert des Doppelten der Helligkeit minus 1 (1 - Math.abs(2 * l - 1)). Sobald wir diese Werte haben, müssen wir sie in Werte von 100 % umrechnen, also multiplizieren wir sie mit 100 und runden auf die nächste Zehntelstelle. Jetzt können wir unser hsl() zusammenfügen.

function RGBToHSL(r,g,b) {
  ...
  // Calculate lightness
  l = (cmax + cmin) / 2;

  // Calculate saturation
  s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
    
  // Multiply l and s by 100
  s = +(s * 100).toFixed(1);
  l = +(l * 100).toFixed(1);

  return "hsl(" + h + "," + s + "%," + l + "%)";
}

HSL aus RGB im String

Für einen einzelnen String teilen wir das Argument durch Komma oder Leerzeichen, entfernen die %s und lokalisieren r, g und b wie zuvor.

function RGBToHSL(rgb) {
  let sep = rgb.indexOf(",") > -1 ? "," : " ";
  rgb = rgb.substr(4).split(")")[0].split(sep);

  for (let R in rgb) {
    let r = rgb[R];
    if (r.indexOf("%") > -1) 
      rgb[R] = Math.round(r.substr(0,r.length - 1) / 100 * 255);
  }

  // Make r, g, and b fractions of 1
  let r = rgb[0] / 255,
      g = rgb[1] / 255,
      b = rgb[2] / 255;

  ...
}

RGBA zu HSLA

Verglichen mit dem, was wir gerade getan haben, um RGB in HSL zu konvertieren, wird das Alpha-Gegenstück im Grunde nichts sein! Wir verwenden einfach den Code für RGB zu HSL (die Mehrargumenten-Version) wieder, lassen a unverändert und übergeben a an das zurückgegebene HSLA. Beachten Sie, dass es zwischen 0 und 1 liegen sollte.

function RGBAToHSLA(r,g,b,a) {
  // Code for RGBToHSL(r,g,b) before return
  ...

  return "hsla(" + h + "," + s + "%," +l + "%," + a + ")";
}

HSLA aus RGBA im String

Für Zeichenfolgen wendet wir die Aufteilungs- und Entfernungslogik erneut an, verwenden aber das vierte Element in rgba für a. Erinnern Sie sich an die neue rgba(r g b / a)-Syntax? Wir verwenden die Akzeptanz davon, so wie wir es für RGBAToHexA() getan haben. Dann ist der Rest des Codes die normale RGB-zu-HSL-Konvertierung.

function RGBAToHSLA(rgba) {
  let sep = rgba.indexOf(",") > -1 ? "," : " ";
  rgba = rgba.substr(5).split(")")[0].split(sep);

  // Strip the slash if using space-separated syntax
  if (rgba.indexOf("/") > -1) 
    rgba.splice(3,1);

  for (let R in rgba) {
    let r = rgba[R];
    if (r.indexOf("%") > -1) {
      let p = r.substr(0,r.length - 1) / 100;

      if (R < 3) { 
        rgba[R] = Math.round(p * 255);
      } else {
        rgba[R] = p;
      }
    }
  }

  // Make r, g, and b fractions of 1
  let r = rgba[0] / 255,
      g = rgba[1] / 255,
      b = rgba[2] / 255,
      a = rgba[3];

  // Rest of RGB-to-HSL logic
  ...
}

Möchten Sie das Alpha so belassen, wie es ist? Entfernen Sie die else-Anweisung aus der for-Schleife.

for (let R in rgba) {
  let r = rgba[R];
  if (r.indexOf("%") > -1) {
    let p = r.substr(0,r.length - 1) / 100;

    if (R < 3) {
      rgba[R] = Math.round(p * 255);
    }
  }
}

HSL zu RGB

Es erfordert etwas weniger Logik, HSL zurück in RGB zu konvertieren als umgekehrt. Da wir einen Bereich von 0–100 für die Sättigung und Helligkeit verwenden, ist der erste Schritt, sie durch 100 zu teilen, um Werte zwischen 0 und 1 zu erhalten. Als Nächstes ermitteln wir Chroma (c), was die Farbintensität ist, also ist das (1 - Math.abs(2 * l - 1)) * s. Dann verwenden wir x für die zweitgrößte Komponente (die erste ist Chroma), den Betrag, der zu jedem Kanal addiert wird, um die Helligkeit anzupassen (m), und initialisieren r, g, b.

function HSLToRGB(h,s,l) {
  // Must be fractions of 1
  s /= 100;
  l /= 100;

  let c = (1 - Math.abs(2 * l - 1)) * s,
      x = c * (1 - Math.abs((h / 60) % 2 - 1)),
      m = l - c/2,
      r = 0,
      g = 0,
      b = 0;
}

Der Farbton bestimmt, wie Rot, Grün und Blau sein sollten, je nachdem, in welchem 60°-Sektor des Farbrads er liegt.

Color wheel
Das Farbrad, aufgeteilt in 60°-Segmente

Dann werden c und x wie unten gezeigt zugewiesen, wobei ein Kanal auf 0 bleibt. Um den endgültigen RGB-Wert zu erhalten, addieren wir m zu jedem Kanal, multiplizieren ihn mit 255 und runden ihn.

function HSLToRGB(h,s,l) {
  ...

  if (0 <= h && h < 60) {
    r = c; g = x; b = 0;  
  } else if (60 <= h && h < 120) {
    r = x; g = c; b = 0;
  } else if (120 <= h && h < 180) {
    r = 0; g = c; b = x;
  } else if (180 <= h && h < 240) {
    r = 0; g = x; b = c;
  } else if (240 <= h && h < 300) {
    r = x; g = 0; b = c;
  } else if (300 <= h && h < 360) {
    r = c; g = 0; b = x;
  }
  r = Math.round((r + m) * 255);
  g = Math.round((g + m) * 255);
  b = Math.round((b + m) * 255);

  return "rgb(" + r + "," + g + "," + b + ")";
}

RGB aus HSL im String

Für die einzelne String-Version modifizieren wir die ersten Zeilen im Grunde auf die gleiche Weise, wie wir es für RGBToHSL(r,g,b) getan haben. Entfernen Sie s /= 100; und l /= 100; und wir verwenden die neuen Anweisungen, um die ersten 4 Zeichen und die ) für unser Array von HSL-Werten zu entfernen, dann die %s von s und l, bevor wir sie durch 100 teilen.

function HSLToRGB(hsl) {
  let sep = hsl.indexOf(",") > -1 ? "," : " ";
  hsl = hsl.substr(4).split(")")[0].split(sep);

  let h = hsl[0],
      s = hsl[1].substr(0,hsl[1].length - 1) / 100,
      l = hsl[2].substr(0,hsl[2].length - 1) / 100;

  ...
}

Die nächsten paar Anweisungen kümmern sich um Farbtöne, die mit einer Einheit angegeben werden – Grad, Radiant oder Umdrehungen. Wir multiplizieren Radiant mit 180/π und Umdrehungen mit 360. Wenn das Ergebnis über 360 liegt, wenden wir einen modularen Divisionsschritt an, um es im Bereich zu halten. All dies geschieht, bevor wir uns mit c, x und m befassen.

function HSLToRGB(hsl) {
  ...

  // Strip label and convert to degrees (if necessary)
  if (h.indexOf("deg") > -1) 
    h = h.substr(0,h.length - 3);
  else if (h.indexOf("rad") > -1)
    h = Math.round(h.substr(0,h.length - 3) * (180 / Math.PI));
  else if (h.indexOf("turn") > -1)
    h = Math.round(h.substr(0,h.length - 4) * 360);
  // Keep hue fraction of 360 if ending up over
  if (h >= 360)
    h %= 360;
    
  // Conversion to RGB begins
  ...
}

Nachdem die obigen Schritte implementiert wurden, können jetzt folgende Werte sicher verwendet werden

  • hsl(180 100% 50%)
  • hsl(180deg,100%,50%)
  • hsl(180deg 100% 50%)
  • hsl(3.14rad,100%,50%)
  • hsl(3.14rad 100% 50%)
  • hsl(0.5turn,100%,50%)
  • hsl(0.5turn 100% 50%)

Puh, das ist eine Menge Flexibilität!

RGB mit %s aus HSL ausgeben

Ähnlich können wir diese Funktion modifizieren, um Prozentwerte zurückzugeben, genau wie wir es in hexToRGB() getan haben.

function HSLToRGB(hsl,isPct) {
  let sep = hsl.indexOf(",") > -1 ? "," : " ";
  hsl = hsl.substr(4).split(")")[0].split(sep);
  isPct = isPct === true;

  ...

  if (isPct) {
    r = +(r / 255 * 100).toFixed(1);
    g = +(g / 255 * 100).toFixed(1);
    b = +(b / 255 * 100).toFixed(1);
  }

  return "rgb("+ (isPct ? r + "%," + g + "%," + b + "%" : +r + "," + +g + "," + +b) + ")";
}

HSLA zu RGBA

Wiederum ist die Handhabung von Alphas ein Kinderspiel. Wir können den Code für die ursprüngliche HSLToRGB(h,s,l)-Funktion wiederverwenden und a zum return hinzufügen.

function HSLAToRGBA(h,s,l,a) {
  // Code for HSLToRGB(h,s,l) before return
  ...

  return "rgba(" + r + "," + g + "," + b + "," + a + ")";
}

RGBA aus HSLA im String

Wenn wir sie auf ein Argument ändern, wird die Art und Weise, wie wir Strings hier behandeln, nicht viel anders sein als das, was wir zuvor getan haben. Eine neue HSLA-Syntax von Colors Level 4 verwendet (value value value / value), genau wie RGBA, sodass wir, wenn wir den Code zur Handhabung haben, etwas wie hsla(210 100% 50% / 0.5) hier einfügen können.

function HSLAToRGBA(hsla) { 
  let sep = hsla.indexOf(",") > -1 ? "," : " ";
  hsla = hsla.substr(5).split(")")[0].split(sep);

  if (hsla.indexOf("/") > -1)
    hsla.splice(3,1);

  let h = hsla[0],
      s = hsla[1].substr(0,hsla[1].length - 1) / 100,
      l = hsla[2].substr(0,hsla[2].length - 1) / 100,
      a = hsla[3];
        
  if (h.indexOf("deg") > -1)
    h = h.substr(0,h.length - 3);
  else if (h.indexOf("rad") > -1)
    h = Math.round(h.substr(0,h.length - 3) * (180 / Math.PI));
  else if (h.indexOf("turn") > -1)
    h = Math.round(h.substr(0,h.length - 4) * 360);
  if (h >= 360)
    h %= 360;

  ...
}

Darüber hinaus sind diese anderen Kombinationen möglich geworden

  • hsla(180,100%,50%,50%)
  • hsla(180 100% 50% / 50%)
  • hsla(180deg,100%,50%,0.5)
  • hsla(3.14rad,100%,50%,0.5)
  • hsla(0.5turn 100% 50% / 50%)

RGBA mit %s aus HSLA

Dann können wir die gleiche Logik zur Ausgabe von Prozenten, einschließlich Alpha, nachbilden. Wenn das Alpha ein Prozent sein soll (gesucht in pctFound), hier ist, wie wir es behandeln können

  1. Wenn r, g und b in Prozente umgewandelt werden sollen, dann muss a mit 100 multipliziert werden, wenn es nicht bereits ein Prozent ist. Andernfalls entfernen Sie das % und es wird im return wieder hinzugefügt.
  2. Wenn r, g und b unverändert bleiben sollen, dann entfernen Sie das % von a und teilen Sie a durch 100.
function HSLAToRGBA(hsla,isPct) {
  // Code up to slash stripping
  ...
    
  isPct = isPct === true;
    
  // h, s, l, a defined to rounding of r, g, b
  ...
    
  let pctFound = a.indexOf("%") > -1;
    
  if (isPct) {
    r = +(r / 255 * 100).toFixed(1);
    g = +(g / 255 * 100).toFixed(1);
    b = +(b / 255 * 100).toFixed(1);
    if (!pctFound) {
      a *= 100;
    } else {
      a = a.substr(0,a.length - 1);
    }
        
  } else if (pctFound) {
    a = a.substr(0,a.length - 1) / 100;
  }

  return "rgba("+ (isPct ? r + "%," + g + "%," + b + "%," + a + "%" : +r + ","+ +g + "," + +b + "," + +a) + ")";
}

Hex zu HSL

Sie denken vielleicht, dass dieser und der nächste Prozess verrückter sind als die anderen, aber sie sind lediglich in zwei Teile mit wiederverwendeter Logik unterteilt. Zuerst konvertieren wir Hex in RGB. Das gibt uns die Basis 10er-Werte, die wir in HSL konvertieren müssen.

function hexToHSL(H) {
  // Convert hex to RGB first
  let r = 0, g = 0, b = 0;
  if (H.length == 4) {
    r = "0x" + H[1] + H[1];
    g = "0x" + H[2] + H[2];
    b = "0x" + H[3] + H[3];
  } else if (H.length == 7) {
    r = "0x" + H[1] + H[2];
    g = "0x" + H[3] + H[4];
    b = "0x" + H[5] + H[6];
  }
  // Then to HSL
  r /= 255;
  g /= 255;
  b /= 255;
  let cmin = Math.min(r,g,b),
      cmax = Math.max(r,g,b),
      delta = cmax - cmin,
      h = 0,
      s = 0,
      l = 0;

  if (delta == 0)
    h = 0;
  else if (cmax == r)
    h = ((g - b) / delta) % 6;
  else if (cmax == g)
    h = (b - r) / delta + 2;
  else
    h = (r - g) / delta + 4;

  h = Math.round(h * 60);

  if (h < 0)
    h += 360;

  l = (cmax + cmin) / 2;
  s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
  s = +(s * 100).toFixed(1);
  l = +(l * 100).toFixed(1);

  return "hsl(" + h + "," + s + "%," + l + "%)";
}

Hex (#rrggbbaa) zu HSLA

Es ändern sich nicht viele Zeilen. Wir wiederholen, was wir kürzlich getan haben, um das Alpha durch Konvertierung des Hex-Werts zu erhalten, aber wir teilen es nicht sofort durch 255. Zuerst müssen wir Farbton, Sättigung und Helligkeit erhalten, wie wir es in den anderen To-HSL-Funktionen getan haben. Dann, vor dem abschließenden return, teilen wir das Alpha und legen die Dezimalstellen fest.

function hexAToHSLA(H) {
  let r = 0, g = 0, b = 0, a = 1;

  if (H.length == 5) {
    r = "0x" + H[1] + H[1];
    g = "0x" + H[2] + H[2];
    b = "0x" + H[3] + H[3];
    a = "0x" + H[4] + H[4];
  } else if (H.length == 9) {
    r = "0x" + H[1] + H[2];
    g = "0x" + H[3] + H[4];
    b = "0x" + H[5] + H[6];
    a = "0x" + H[7] + H[8];
  }

  // Normal conversion to HSL
  ...
        
  a = (a / 255).toFixed(3);
                
  return "hsla("+ h + "," + s + "%," + l + "%," + a + ")";
}

HSL zu Hex

Diese beginnt als Konvertierung zu RGB, aber es gibt einen zusätzlichen Schritt für das Math.round() bei der Konvertierung der RGB-Ergebnisse in Hex.

function HSLToHex(h,s,l) {
  s /= 100;
  l /= 100;

  let c = (1 - Math.abs(2 * l - 1)) * s,
      x = c * (1 - Math.abs((h / 60) % 2 - 1)),
      m = l - c/2,
      r = 0,
      g = 0, 
      b = 0; 

  if (0 <= h && h < 60) {
    r = c; g = x; b = 0;
  } else if (60 <= h && h < 120) {
    r = x; g = c; b = 0;
  } else if (120 <= h && h < 180) {
    r = 0; g = c; b = x;
  } else if (180 <= h && h < 240) {
    r = 0; g = x; b = c;
  } else if (240 <= h && h < 300) {
    r = x; g = 0; b = c;
  } else if (300 <= h && h < 360) {
    r = c; g = 0; b = x;
  }
  // Having obtained RGB, convert channels to hex
  r = Math.round((r + m) * 255).toString(16);
  g = Math.round((g + m) * 255).toString(16);
  b = Math.round((b + m) * 255).toString(16);

  // Prepend 0s, if necessary
  if (r.length == 1)
    r = "0" + r;
  if (g.length == 1)
    g = "0" + g;
  if (b.length == 1)
    b = "0" + b;

  return "#" + r + g + b;
}

Hex aus HSL im String

Selbst die ersten Zeilen dieser Funktion werden wie die von HSLToRGB() sein, wenn wir sie so geändert haben, dass sie einen einzelnen String akzeptiert. So erhalten wir überhaupt erst den Farbton, die Sättigung und die Helligkeit separat. Vergessen wir nicht den Schritt, das Farbton-Label zu entfernen und in Grad umzurechnen. All dies wird anstelle von s /= 100; und l /= 100; stehen.

function HSLToHex(hsl) { 
  let sep = hsl.indexOf(",") > -1 ? "," : " ";
  hsl = hsl.substr(4).split(")")[0].split(sep);

  let h = hsl[0],
      s = hsl[1].substr(0,hsl[1].length - 1) / 100,
      l = hsl[2].substr(0,hsl[2].length - 1) / 100;
        
  // Strip label and convert to degrees (if necessary)
  if (h.indexOf("deg") > -1)
    h = h.substr(0,h.length - 3);
  else if (h.indexOf("rad") > -1)
    h = Math.round(h.substr(0,h.length - 3) * (180 / Math.PI));
  else if (h.indexOf("turn") > -1)
    h = Math.round(h.substr(0,h.length - 4) * 360);
  if (h >= 360)
    h %= 360;

  ...
}

HSLA zu Hex (#rrggbbaa)

Durch Hinzufügen von Alpha zum Mix konvertieren wir a zu Hex und fügen ein viertes if hinzu, um bei Bedarf eine 0 voranzustellen. Mit dieser Logik sind Sie wahrscheinlich bereits vertraut, da wir sie zuletzt in RGBAToHexA() verwendet haben.

function HSLAToHexA(h,s,l,a) {
  // Repeat code from HSLToHex(h,s,l) until 3 `toString(16)`s
  ...

  a = Math.round(a * 255).toString(16);

  if (r.length == 1)
    r = "0" + r;
  if (g.length == 1)
    g = "0" + g;
  if (b.length == 1)
    b = "0" + b;
  if (a.length == 1)
    a = "0" + a;

  return "#" + r + g + b + a;
}

Hex (#rrggbbaa) aus HSLA im String

Schließlich sind die Zeilen der Version mit einem Argument bis zu a = hsla[3] nicht anders als die der HSLAToRGBA().

function HSLAToHexA(hsla) {
  let sep = hsla.indexOf(",") > -1 ? "," : " ";
  hsla = hsla.substr(5).split(")")[0].split(sep);
    
  // Strip the slash
  if (hsla.indexOf("/") > -1)
    hsla.splice(3,1);
    
  let h = hsla[0],
      s = hsla[1].substr(0,hsla[1].length - 1) / 100,
      l = hsla[2].substr(0,hsla[2].length - 1) / 100,
      a = hsla[3];
            
  ...
}

Eingebaute Namen

Um eine benannte Farbe in RGB, Hex oder HSL zu konvertieren, könnten Sie in Erwägung ziehen, diese Tabelle mit über 140 Namen und Hex-Werten zu Beginn in ein riesiges Objekt zu verwandeln. Die Wahrheit ist, dass wir es wirklich nicht brauchen, denn hier ist, was wir tun können

  1. Erstellen Sie ein Element
  2. Geben Sie ihm eine Textfarbe
  3. Holen Sie sich den Wert dieser Eigenschaft
  4. Entfernen Sie das Element
  5. Geben Sie den gespeicherten Farbwert zurück, der standardmäßig RGB sein wird

Unsere Funktion zum Abrufen von RGB besteht also nur aus sieben Zeilen!

function nameToRGB(name) {
  // Create fake div
  let fakeDiv = document.createElement("div");
  fakeDiv.style.color = name;
  document.body.appendChild(fakeDiv);

  // Get color of div
  let cs = window.getComputedStyle(fakeDiv),
      pv = cs.getPropertyValue("color");

  // Remove div after obtaining desired color value
  document.body.removeChild(fakeDiv);

  return pv;
}

Gehen wir noch weiter. Was wäre, wenn wir die Ausgabe stattdessen in Hex ändern?

function nameToHex(name) {
  // Get RGB from named color in temporary div
  let fakeDiv = document.createElement("div");
  fakeDiv.style.color = name;
  document.body.appendChild(fakeDiv);

  let cs = window.getComputedStyle(fakeDiv),
      pv = cs.getPropertyValue("color");

  document.body.removeChild(fakeDiv);

  // Code ripped from RGBToHex() (except pv is substringed)
  let rgb = pv.substr(4).split(")")[0].split(","),
      r = (+rgb[0]).toString(16),
      g = (+rgb[1]).toString(16),
      b = (+rgb[2]).toString(16);

  if (r.length == 1)
    r = "0" + r;
  if (g.length == 1)
    g = "0" + g;
  if (b.length == 1)
    b = "0" + b;

  return "#" + r + g + b;
}

Oder, warum nicht HSL? 😉

function nameToHSL(name) {
  let fakeDiv = document.createElement("div");
  fakeDiv.style.color = name;
  document.body.appendChild(fakeDiv);

  let cs = window.getComputedStyle(fakeDiv),
      pv = cs.getPropertyValue("color");

  document.body.removeChild(fakeDiv);

  // Code ripped from RGBToHSL() (except pv is substringed)
  let rgb = pv.substr(4).split(")")[0].split(","),
      r = rgb[0] / 255,
      g = rgb[1] / 255,
      b = rgb[2] / 255,
      cmin = Math.min(r,g,b),
      cmax = Math.max(r,g,b),
      delta = cmax - cmin,
      h = 0,
      s = 0,
      l = 0;

  if (delta == 0)
    h = 0;
  else if (cmax == r)
    h = ((g - b) / delta) % 6;
  else if (cmax == g)
    h = (b - r) / delta + 2;
  else
    h = (r - g) / delta + 4;

  h = Math.round(h * 60);

  if (h < 0)
    h += 360;

  l = (cmax + cmin) / 2;
  s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
  s = +(s * 100).toFixed(1);
  l = +(l * 100).toFixed(1);

  return "hsl(" + h + "," + s + "%," + l + "%)";
}

Auf lange Sicht wird jede Konvertierung von einem Namen nach der Dekodierung des Namens zu einer Konvertierung von RGB.

Validierung von Farben

In all diesen Funktionen gab es keine Maßnahmen, um lächerliche Eingaben (z. B. Farbtöne über 360 oder Prozente über 100) zu verhindern oder zu korrigieren. Wenn wir nur Pixel auf einem manipulieren, das mit getImageData() abgerufen wurde, ist die Validierung von Farbwerten vor der Konvertierung nicht notwendig, da sie unabhängig vom Input korrekt sind. Wenn wir ein Farbkonvertierungstool erstellen, bei dem Benutzer die Farbe angeben, wäre eine Validierung jedoch dringend erforderlich.

Es ist einfach, fehlerhafte Eingaben für Kanäle als separate Argumente zu behandeln, z. B. für RGB

// Correct red
if (r > 255)
  r = 255;
else if (r < 0)
  r = 0;

Wenn eine ganze Zeichenfolge validiert wird, dann ist ein regulärer Ausdruck erforderlich. Zum Beispiel ist dies die Funktion RGBToHex(), der eine Validierungsstufe mit einem Ausdruck hinzugefügt wurde

function RGBToHex(rgb) {
  // Expression for rgb() syntaxes
  let ex = /^rgb\((((((((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5]),\s?)){2}|((((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5])\s)){2})((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5]))|((((([1-9]?\d(\.\d+)?)|100|(\.\d+))%,\s?){2}|((([1-9]?\d(\.\d+)?)|100|(\.\d+))%\s){2})(([1-9]?\d(\.\d+)?)|100|(\.\d+))%))\)$/i;

  if (ex.test(rgb)) {
    // Logic to convert RGB to hex
    ...

  } else {
    // Something to do if color is invalid
  }
}

Um andere Arten von Werten zu testen, finden Sie unten eine Tabelle mit Ausdrücken, die sowohl opake als auch Alpha-aktivierte Werte abdecken

FarbwertRegEx
RGB/^rgb\((((((((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5]),\s?)){2}|((((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5])\s)){2})((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5]))|((((([1-9]?\d(\.\d+)?)|100|(\.\d+))%,\s?){2}|((([1-9]?\d(\.\d+)?)|100|(\.\d+))%\s){2})(([1-9]?\d(\.\d+)?)|100|(\.\d+))%))\)$/i
RGBA/^rgba\((((((((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5]),\s?)){3})|(((([1-9]?\d(\.\d+)?)|100|(\.\d+))%,\s?){3}))|(((((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5])\s){3})|(((([1-9]?\d(\.\d+)?)|100|(\.\d+))%\s){3}))\/\s)((0?\.\d+)|[01]|(([1-9]?\d(\.\d+)?)|100|(\.\d+))%)\)$/i
Hex/^#([\da-f]{3}){1,2}$/i
Hex (mit Alpha)/^#([\da-f]{4}){1,2}$/i
HSL/^hsl\(((((([12]?[1-9]?\d)|[12]0\d|(3[0-5]\d))(\.\d+)?)|(\.\d+))(deg)?|(0|0?\.\d+)turn|(([0-6](\.\d+)?)|(\.\d+))rad)((,\s?(([1-9]?\d(\.\d+)?)|100|(\.\d+))%){2}|(\s(([1-9]?\d(\.\d+)?)|100|(\.\d+))%){2})\)$/i
HSLA/^hsla\(((((([12]?[1-9]?\d)|[12]0\d|(3[0-5]\d))(\.\d+)?)|(\.\d+))(deg)?|(0|0?\.\d+)turn|(([0-6](\.\d+)?)|(\.\d+))rad)(((,\s?(([1-9]?\d(\.\d+)?)|100|(\.\d+))%){2},\s?)|((\s(([1-9]?\d(\.\d+)?)|100|(\.\d+))%){2}\s\/\s))((0?\.\d+)|[01]|(([1-9]?\d(\.\d+)?)|100|(\.\d+))%)\)$/i

Wenn man sich die Ausdrücke für RGB(A) und HSL(A) ansieht, werden Sie wahrscheinlich große Augen bekommen; diese wurden umfassend genug erstellt, um die meisten neuen Syntaxen von CSS Colors Level 4 abzudecken. Hex hingegen benötigt keine Ausdrücke wie die anderen, da es nur auf Ziffernanzahlen ankommt. Gleich werden wir diese zerlegen und die Teile entschlüsseln. Beachten Sie, dass Groß-/Kleinschreibung ignorierende Werte (/i) alle diese bestehen.

RGB

/^rgb\((((((((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5]),\s?)){2}|((((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5])\s)){2})((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5]))|((((([1-9]?\d(\.\d+)?)|100|(\.\d+))%,\s?){2}|((([1-9]?\d(\.\d+)?)|100|(\.\d+))%\s){2})(([1-9]?\d(\.\d+)?)|100|(\.\d+))%))\)$/i

Da rgb() entweder nur ganze Zahlen oder nur Prozente akzeptiert, sind beide Fälle abgedeckt. In der äußersten Gruppe, zwischen ^rgb\( und \)$, gibt es innere Gruppen für sowohl ganze Zahlen als auch Prozente, alle mit Komma-Leerzeichen oder nur Leerzeichen als Trennzeichen.

  1. (((((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5]),\s?){2}|(((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5])\s){2})((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5]))
  2. ((((([1-9]?\d(\.\d+)?)|100|(\.\d+))%,\s?){2}|((([1-9]?\d(\.\d+)?)|100|(\.\d+))%\s){2})(([1-9]?\d(\.\d+)?)|100|(\.\d+))%)

Im ersten Teil akzeptieren wir zwei Instanzen von ganzen Zahlen für Rot und Grün von 0–99 oder 111-199 ((1?[1-9]?\d)), 100–109 (10\d), 200-249 ((2[0-4]\d)) oder 250–255 (25[0-5]). Wir konnten nicht einfach \d{1,3} verwenden, da Werte wie 03 oder 017 und solche über 255 nicht erlaubt sein sollten. Danach folgt das Komma und optionales Leerzeichen (,\s?). Auf der anderen Seite des |, nach dem ersten {2} (was zwei Instanzen von ganzen Zahlen angibt), prüfen wir auf dasselbe mit Leerzeichen-Trennung, wenn die linke Seite falsch ist. Dann sollte für Blau dasselbe akzeptiert werden, aber ohne Trennzeichen.

Im anderen Teil sollten akzeptable Werte für Prozente, einschließlich Fließkommazahlen, entweder 0–99, explizit 100 und keine Fließkommazahl oder Fließkommazahlen unter 1 mit der weggelassenen 0 sein. Daher ist das Segment hier (([1-9]?\d(\.\d+)?)|100|(\.\d+)), und es erscheint dreimal; zweimal mit Trennzeichen ((,\s?){2}, %\s){2}), einmal ohne.

Es ist legal, Prozente ohne Leerzeichen-Trennzeichen zu verwenden (z. B. rgb(100%50%10%)) in CSS, aber die von uns geschriebenen Funktionen unterstützen das nicht. Dasselbe gilt für rgba(100%50%10%/50%), hsl(40 100%50%) und hsla(40 100%50%/0.5). Dies könnte durchaus ein Vorteil für Code-Golf und Minifizierung sein!

RGBA

/^rgba\((((((((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5]),\s?)){3})|(((([1-9]?\d(\.\d+)?)|100|(\.\d+))%,\s?){3}))|(((((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5])\s){3})|(((([1-9]?\d(\.\d+)?)|100|(\.\d+))%\s){3}))\/\s)((0?\.\d+)|[01]|(([1-9]?\d(\.\d+)?)|100|(\.\d+))%)\)$/i

Der nächste Ausdruck ist dem vorherigen sehr ähnlich, aber drei Instanzen von ganzen Zahlen (((((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5]),\s?){3})) oder Prozentsätzen ((((([1-9]?\d(\.\d+)?)|100|(\.\d+))%,\s?){3})), plus ein optionales Komma und Leerzeichen werden geprüft. Andernfalls sucht er nach demselben, aber mit Leerzeichen-Trennzeichen, plus einem Schrägstrich und einem Leerzeichen (\/\s) nach dem Blau. Daneben steht ((0?\.\d+)|[01]|(([1-9]?\d(\.\d+)?)|100|(\.\d+))%), wobei wir Floats mit oder ohne die erste Null ((0?\.\d+)), 0 oder 1 ([01]) am Punkt oder 0–100% ((([1-9]?\d(\.\d+)?)|100|(\.\d+))%) akzeptieren.

Hex mit Alpha

// #rgb/#rrggbb
/^#([\da-f]{3}){1,2}$/i
// #rgba/#rrggbbaa
/^#([\da-f]{4}){1,2}$/i

Für Hexadezimalzahlen – sowohl mit als auch ohne Alpha – werden Zahlen oder Buchstaben a–f ([\da-f]) akzeptiert. Dann wird entweder eine oder zwei Instanzen davon für kurz- oder langgeschriebene Werte gezählt (#rgb oder #rrggbb). Als Veranschaulichung haben wir dieses gleiche kurze Muster: /^#([\da-f]{n}){1,2}$/i. Ändern Sie einfach n zu 3 oder 4.

HSL und HSLA

// HSL
/^hsl\((((((\[12]?[1-9]?\d)|[12]0\d|(3[0-5]\d))(\.\d+)?)|(\.\d+))(deg)?|(0|0?\.\d+)turn|(([0-6\\.\d+)?)|(\.\d+))rad)((,\s?(([1-9]?\d(\.\d+)?)|100|(\.\d+))%){2}|(\s(([1-9]?\d(\.\d+)?)|100|(\.\d+))%){2})\)$/i
// HSLA
/^hsla\((((((\[12]?[1-9]?\d)|[12]0\d|(3[0-5]\d))(\.\d+)?)|(\.\d+))(deg)?|(0|0?\.\d+)turn|(([0-6\\.\d+)?)|(\.\d+))rad)(((,\s?(([1-9]?\d(\.\d+)?)|100|(\.\d+))%){2},\s?)|((\s(([1-9]?\d(\.\d+)?)|100|(\.\d+))%){2}\s\/\s))((0?\.\d+)|[01]|(([1-9]?\d(\.\d+)?)|100|(\.\d+))%)\)$/i

Nach der \( in beiden Ausdrücken für HSL und HSLA, ist dieser große Teil für den Farbton

(((((\[12]?[1-9]?\d)|[12]0\d|(3[0-5]\d))(\.\d+)?)|(\.\d+))(deg)?|(0|0?\.\d+)turn|(([0-6\\.\d+)?)|(\.\d+))rad)

([12]?[1-9]?\d) deckt 0–99, 110–199 und 210–299 ab. [12]0\d deckt 110–109 und 200–209 ab. Dann kümmert sich (3[0-5]\d) um 300–359. Der Grund für diese Aufteilung der Bereiche ähnelt dem von ganzen Zahlen in der rgb()-Syntax: Ausschluss von führenden Nullen und Werten über dem Maximum. Da Töne Fließkommazahlen sein können, ist das erste (\.\d+)? dafür vorgesehen.

Neben dem | nach dem genannten Code-Segment ist das zweite (\.\d+) für Floats ohne führende Null.

Nun bewegen wir uns eine Ebene höher und entschlüsseln den nächsten kleinen Teil

(deg)?|(0|0?\.\d+)turn|((\[0-6\\.\d+)?)|(\.\d+))rad

Dieser enthält die Bezeichnungen, die wir für den Farbton verwenden können – Grad, Umdrehungen oder Radiant. Wir können alle oder keine deg einschließen. Werte in turn müssen unter 1 liegen. Für Radiant können wir jeden Float zwischen 0 und 7 akzeptieren. Wir wissen jedoch, dass eine 360°-Umdrehung 2π entspricht und ungefähr bei 6,28 endet. Man könnte denken, dass 6,3 und höher nicht akzeptiert werden sollten. Da 2π eine irrationale Zahl ist, wäre es für dieses Beispiel zu unübersichtlich, jede Dezimalstelle zu berücksichtigen, die von der JavaScript-Konsole bereitgestellt wird. Außerdem haben wir diesen Ausschnitt in unseren HSLTo_()-Funktionen als zweite Sicherheitsebene, falls Töne von 360° oder mehr auftreten sollten

// Keep hue fraction of 360 if ending up over
if (h >= 360)
  h %= 360;

Nun bewegen wir uns eine Ebene höher und entschlüsseln den zweiten Teil

(,\s?(([1-9]?\d(\.\d+)?)|100|(\.\d+))%){2}

Wir zählen zwei Instanzen von Komma-Leerzeichen-Prozentsätzen für die Sättigung und Helligkeit (Leerzeichen optional). In der Gruppe nach dem ,\s? prüfen wir auf Werte von 0–99 mit oder ohne Dezimalpunkte (([1-9]?\d(\.\d+)?)), genau 100 oder Floats unter 1 ohne führende 0 ((\.\d+)).

Der letzte Teil des HSL-Ausdrucks, vor dem Ende (\)$/i), ist ein ähnlicher Ausdruck, wenn Leerzeichen das einzige Trennzeichen sind

(\s(([1-9]?\d(\.\d+)?)|100|(\.\d+))%){2}

\s steht am Anfang anstelle von ,\s?. Dann steht in HSLA-Ausdruck dieser gleiche Teil in einer weiteren Gruppe mit ,\s? nach seinem {2}.

((,\s?(([1-9]?\d(\.\d+)?)|100|(\.\d+))%){2},\s?)

Das zählt das Komma-Leerzeichen zwischen Helligkeit und Alpha. Dann müssen wir, wenn Leerzeichen als Trennzeichen verwendet werden, nach dem Zählen von zwei Instanzen von Leerzeichen und einem Prozentsatz auf ein Leerzeichen-Schrägstrich-Leerzeichen (\s\/\s) prüfen.

((\s(([1-9]?\d(\.\d+)?)|100|(\.\d+))%){2}\s\/\s))

Danach müssen wir noch den Alpha-Wert prüfen

(((0?\.\d+)|[01])|(([1-9]?\d(\.\d+)?)|100|(\.\d+))%)

Übereinstimmungen für (0?\.\d+) umfassen Floats unter 1 mit oder ohne führende Null, 0 oder 1 für [01] und 0–100%.

Fazit

Wenn Ihre aktuelle Herausforderung darin besteht, einen Farbraum in einen anderen umzuwandeln, haben Sie jetzt einige Ideen, wie Sie vorgehen können. Da es mühsam wäre, jede jemals erfundene Farbkonvertierung in einem Beitrag zu behandeln, haben wir die praktischsten und browserunterstützten besprochen. Wenn Sie über unterstützte Farbräume hinausgehen möchten (z. B. CMYK, XYZ oder CIE L*a*b*), bietet EasyRGB eine erstaunliche Sammlung von code-fertigen Formeln.

Um alle hier gezeigten Konvertierungen zu sehen, habe ich eine CodePen-Demo erstellt, die Eingaben und Ausgaben in einer Tabelle anzeigt. Sie können verschiedene Farben in den Zeilen 2–10 ausprobieren und die vollständigen Funktionen im JavaScript-Panel sehen.