PHP函数将HSL转换为RGB或十六进制


PHP function to convert HSL to RGB or Hex

有人知道一个PHP函数(适用于>5.3)可以将HSL颜色转换为RGB或Hex吗?我尝试了十几次谷歌搜索,但没有一项功能像预期的那样奏效。

函数转换为RGB还是十六进制并不重要,因为这两者之间的转换很简单。输入是CSS的HSL值(色调:0-360,饱和度:0-100,亮度:0-100)。

编辑:指定输入和输出格式将是一个好处:)

从Jim评论链接中的一个答案中提取代码(PHP HSV到RGB公式理解),我们可以如下计算:

<?php    
    $hue = 209;
    $sat = 75;
    $lum = 60;
    $hue /= 360;
    $sat /= 100;
    $lum /= 100;
    $result = ColorHSLToRGB($hue, $sat, $lum);
    var_dump($result); echo '<br>';
    printf("rgb = %d,%d,%d<br>", $result['r'], $result['g'], $result['b']);


function ColorHSLToRGB($h, $s, $l){
        $r = $l;
        $g = $l;
        $b = $l;
        $v = ($l <= 0.5) ? ($l * (1.0 + $s)) : ($l + $s - $l * $s);
        if ($v > 0){
              $m;
              $sv;
              $sextant;
              $fract;
              $vsf;
              $mid1;
              $mid2;
              $m = $l + $l - $v;
              $sv = ($v - $m ) / $v;
              $h *= 6.0;
              $sextant = floor($h);
              $fract = $h - $sextant;
              $vsf = $v * $sv * $fract;
              $mid1 = $m + $vsf;
              $mid2 = $v - $vsf;
              switch ($sextant)
              {
                    case 0:
                          $r = $v;
                          $g = $mid1;
                          $b = $m;
                          break;
                    case 1:
                          $r = $mid2;
                          $g = $v;
                          $b = $m;
                          break;
                    case 2:
                          $r = $m;
                          $g = $v;
                          $b = $mid1;
                          break;
                    case 3:
                          $r = $m;
                          $g = $mid2;
                          $b = $v;
                          break;
                    case 4:
                          $r = $mid1;
                          $g = $m;
                          $b = $v;
                          break;
                    case 5:
                          $r = $v;
                          $g = $m;
                          $b = $mid2;
                          break;
              }
        }
        return array('r' => $r * 255.0, 'g' => $g * 255.0, 'b' => $b * 255.0);
}
?>

输出:

array(3) { ["r"]=> float(76.5) ["g"]=> float(155.55) ["b"]=> float(229.5) } 
rgb = 76,155,229

把这些放在一起(这帮助我制作了这个图表)

/**
 * convert a HSL colorscheme to either Hexadecimal (default) or RGB.
 * 
 * We want a method where we can programmatically generate a series of colors
 * between two values (eg. red to green) which is easy to do with HSL because
 * you just change the hue. (0 = red, 120 = green).  You can use this function
 * to convert those hsl color values to either the rgb or hexadecimal color scheme.
 * e.g. You have
 *   hsl(50, 100%, 50%)
 * To convert,
 * $hex = convertHSL(50,100,50);  // returns #ffd500
 * or 
 * $rgb = convertHSL(50,100,50, false);  // returns rgb(255, 213, 0)
 *  
 * see https://coderwall.com/p/dvsxwg/smoothly-transition-from-green-to-red
 * @param int $h the hue
 * @param int $s the saturation
 * @param int $l the luminance
 * @param bool $toHex whether you want hexadecimal equivalent or rgb equivalent
 * @return string usable in HTML or CSS
 */
function convertHSL($h, $s, $l, $toHex=true){
    $h /= 360;
    $s /=100;
    $l /=100;
    $r = $l;
    $g = $l;
    $b = $l;
    $v = ($l <= 0.5) ? ($l * (1.0 + $s)) : ($l + $s - $l * $s);
    if ($v > 0){
          $m;
          $sv;
          $sextant;
          $fract;
          $vsf;
          $mid1;
          $mid2;
          $m = $l + $l - $v;
          $sv = ($v - $m ) / $v;
          $h *= 6.0;
          $sextant = floor($h);
          $fract = $h - $sextant;
          $vsf = $v * $sv * $fract;
          $mid1 = $m + $vsf;
          $mid2 = $v - $vsf;
          switch ($sextant)
          {
                case 0:
                      $r = $v;
                      $g = $mid1;
                      $b = $m;
                      break;
                case 1:
                      $r = $mid2;
                      $g = $v;
                      $b = $m;
                      break;
                case 2:
                      $r = $m;
                      $g = $v;
                      $b = $mid1;
                      break;
                case 3:
                      $r = $m;
                      $g = $mid2;
                      $b = $v;
                      break;
                case 4:
                      $r = $mid1;
                      $g = $m;
                      $b = $v;
                      break;
                case 5:
                      $r = $v;
                      $g = $m;
                      $b = $mid2;
                      break;
          }
    }
    $r = round($r * 255, 0);
    $g = round($g * 255, 0);
    $b = round($b * 255, 0);
    if ($toHex) {
        $r = ($r < 15)? '0' . dechex($r) : dechex($r);
        $g = ($g < 15)? '0' . dechex($g) : dechex($g);
        $b = ($b < 15)? '0' . dechex($b) : dechex($b);
        return "#$r$g$b";
    } else {
        return "rgb($r, $g, $b)";    
    }

我对所有其他实现的测试只显示了奇怪且可能令人不快的结果。以下是@Mohsen代码的PHP实现https://stackoverflow.com/a/9493060/1598477.再加上一个测试来展示全部的美丽。。。

很抱歉交叉发布。但我真的没有看到任何其他实现能提供我所需要的质量。

/**
 * Converts an HSL color value to RGB. Conversion formula
 * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
 * Assumes h, s, and l are contained in the set [0, 1] and
 * returns r, g, and b in the set [0, 255].
 *
 * @param   {number}  h       The hue
 * @param   {number}  s       The saturation
 * @param   {number}  l       The lightness
 * @return  {Array}           The RGB representation
 */
function hue2rgb($p, $q, $t){
            if($t < 0) $t += 1;
            if($t > 1) $t -= 1;
            if($t < 1/6) return $p + ($q - $p) * 6 * $t;
            if($t < 1/2) return $q;
            if($t < 2/3) return $p + ($q - $p) * (2/3 - $t) * 6;
            return $p;
        }
function hslToRgb($h, $s, $l){
    if($s == 0){
        $r = $l;
        $g = $l;
        $b = $l; // achromatic
    }else{
        $q = $l < 0.5 ? $l * (1 + $s) : $l + $s - $l * $s;
        $p = 2 * $l - $q;
        $r = hue2rgb($p, $q, $h + 1/3);
        $g = hue2rgb($p, $q, $h);
        $b = hue2rgb($p, $q, $h - 1/3);
    }
    return array(round($r * 255), round($g * 255), round($b * 255));
}
/* Uncomment to test * /
for ($i=0;$i<360;$i++) {
  $rgb=hslToRgb($i/360, 1, .9);
  echo '<div style="background-color:rgb(' .$rgb[0] . ', ' . $rgb[1] . ', ' . $rgb[2] . ');padding:2px;"></div>';
}
/* End Test */

PEAR包Image_Color2具有在颜色模型之间转换的方法-请参阅convertTo。

这是我的解决方案

HSV值限制:$H[0-59]、$S[0-100]、$V[0-100]

function hsv_to_rgb($iH, $iS, $iV) {
    if($iH < 0)   $iH = 0;
    if($iH > 360) $iH = 360;
    if($iS < 0)   $iS = 0;
    if($iS > 100) $iS = 100;
    if($iV < 0)   $iV = 0;
    if($iV > 100) $iV = 100;
    $dS = $iS/100.0;
    $dV = $iV/100.0;
    $dC = $dV*$dS;
    $dH = $iH/60.0;
    $dT = $dH;
    while($dT >= 2.0) $dT -= 2.0; // php modulus does not work with float
    $dX = $dC*(1-abs($dT-1));     // as used in the Wikipedia link
    switch($dH) {
      case($dH >= 0.0 && $dH < 1.0):
        $dR = $dC; $dG = $dX; $dB = 0.0; break;
      case($dH >= 1.0 && $dH < 2.0):
        $dR = $dX; $dG = $dC; $dB = 0.0; break;
      case($dH >= 2.0 && $dH < 3.0):
        $dR = 0.0; $dG = $dC; $dB = $dX; break;
      case($dH >= 3.0 && $dH < 4.0):
        $dR = 0.0; $dG = $dX; $dB = $dC; break;
      case($dH >= 4.0 && $dH < 5.0):
        $dR = $dX; $dG = 0.0; $dB = $dC; break;
      case($dH >= 5.0 && $dH < 6.0):
        $dR = $dC; $dG = 0.0; $dB = $dX; break;
      default:
        $dR = 0.0; $dG = 0.0; $dB = 0.0; break;
    }
    $dM  = $dV - $dC;
    $dR += $dM; $dG += $dM; $dB += $dM;
    $dR *= 255; $dG *= 255; $dB *= 255;
    return array(round($dR), round($dG), round($dB));
  }

由Cullub从该SO应答中复制。

该代码完美地将用户电子邮件转换为用户头像颜色。经过长时间的搜索,我得到了正确的十六进制值,类似于前端使用javascript生成的值。

TL;DR:完整的代码可以在Pastebin上找到。

/**
 * convert user email to hsl for user avatar
 * @param string $string
 * @return string HEX color code
 */
function stringToColor($string)
{
    $hash = 0;
    $l = 70;
    $s = 60;
    for ($i = 0; $i < strlen($string); $i++) {
        $hash = ord($string[$i]) + (($hash << 5) - $hash);
    }
    $h = fmod($hash, 360);
    return $this->hslToHex($h, $s, $l, true);
}
/**
 * Converts HSL to Hex by converting it to 
 * RGB, then converting that to hex.
 * 
 * string hslToHex($h, $s, $l[, $prependPound = true]
 * 
 * $h is the Degrees value of the Hue
 * $s is the Percentage value of the Saturation
 * $l is the Percentage value of the Lightness
 * $prependPound is a bool, whether you want a pound 
 *  sign prepended. (optional - default=true)
 *
 * Calls: 
 *   hslToRgb
 *
 * Output: Hex in the format: #00ff88 (with 
 * pound sign).  Rounded to the nearest whole
 * number.
 */
function hslToHex($h, $s, $l, $prependPound = true)
{
    //convert hsl to rgb
    $rgb = $this->hslToRgb($h, $s, $l);
    //convert rgb to hex
    $hexR = $rgb['r'];
    $hexG = $rgb['g'];
    $hexB = $rgb['b'];
    //round to the nearest whole number
    $hexR = round($hexR);
    $hexG = round($hexG);
    $hexB = round($hexB);
    //convert to hex
    $hexR = dechex($hexR);
    $hexG = dechex($hexG);
    $hexB = dechex($hexB);
    //check for a non-two string length
    //if it's 1, we can just prepend a
    //0, but if it is anything else non-2,
    //it must return false, as we don't 
    //know what format it is in.
    if (strlen($hexR) != 2) {
        if (strlen($hexR) == 1) {
            //probably in format #0f4, etc.
            $hexR = "0" . $hexR;
        } else {
            //unknown format
            return false;
        }
    }
    if (strlen($hexG) != 2) {
        if (strlen($hexG) == 1) {
            $hexG = "0" . $hexG;
        } else {
            return false;
        }
    }
    if (strlen($hexB) != 2) {
        if (strlen($hexB) == 1) {
            $hexB = "0" . $hexB;
        } else {
            return false;
        }
    }
    //if prependPound is set, will prepend a
    //# sign to the beginning of the hex code.
    //(default = true)
    $hex = "";
    if ($prependPound) {
        $hex = "#";
    }
    $hex = $hex . $hexR . $hexG . $hexB;
    return $hex;
}
/**
 * Converts an HSL color value to RGB. Conversion formula
 * adapted from http://www.niwa.nu/2013/05/math-behind-colorspace-conversions-rgb-hsl/.
 * Assumes h, s, and l are in the format Degrees,
 * Percent, Percent, and returns r, g, and b in 
 * the range [0 - 255].
 *
 * Called by hslToHex by default.
 *
 * Calls: 
 *   degPercPercToHsl
 *   hueToRgb
 *
 * @param   Number  h       The hue value
 * @param   Number  s       The saturation level
 * @param   Number  l       The luminence
 * @return  Array           The RGB representation
 */
function hslToRgb($h, $s, $l)
{
    //convert the hue's 360 degrees in a circle to 1
    $h /= 360;
    //convert the saturation and lightness to the 0-1 
    //range by multiplying by 100
    $s /= 100;
    $l /= 100;
    //If there's no saturation, the color is a greyscale,
    //so all three RGB values can be set to the lightness.
    //(Hue doesn't matter, because it's grey, not color)
    if ($s == 0) {
        $r = $l * 255;
        $g = $l * 255;
        $b = $l * 255;
    } else {
        //calculate some temperary variables to make the 
        //calculation eaisier.
        if ($l < 0.5) {
            $temp2 = $l * (1 + $s);
        } else {
            $temp2 = ($l + $s) - ($s * $l);
        }
        $temp1 = 2 * $l - $temp2;
        //run the calculated vars through hueToRgb to
        //calculate the RGB value.  Note that for the Red
        //value, we add a third (120 degrees), to adjust 
        //the hue to the correct section of the circle for
        //red.  Simalarly, for blue, we subtract 1/3.
        $r = 255 * $this->hueToRgb($temp1, $temp2, $h + (1 / 3));
        $g = 255 * $this->hueToRgb($temp1, $temp2, $h);
        $b = 255 * $this->hueToRgb($temp1, $temp2, $h - (1 / 3));
    }
    $rgb['r'] = $r;
    $rgb['g'] = $g;
    $rgb['b'] = $b;
    return $rgb;
    // return "rgb($r, $g, $b)";
}
/**
 * Converts an HSL hue to it's RGB value.  
 *
 * Input: $temp1 and $temp2 - temperary vars based on 
 * whether the lumanence is less than 0.5, and 
 * calculated using the saturation and luminence
 * values.
 *  $hue - the hue (to be converted to an RGB 
 * value)  For red, add 1/3 to the hue, green 
 * leave it alone, and blue you subtract 1/3 
 * from the hue.
 *
 * Output: One RGB value.
 *
 * Thanks to Easy RGB for this function (Hue_2_RGB).
 * http://www.easyrgb.com/index.php?X=MATH&$h=19#text19
 *
 */
function hueToRgb($temp1, $temp2, $hue)
{
    if ($hue < 0) {
        $hue += 1;
    }
    if ($hue > 1) {
        $hue -= 1;
    }
    if ((6 * $hue) < 1) {
        return ($temp1 + ($temp2 - $temp1) * 6 * $hue);
    } elseif ((2 * $hue) < 1) {
        return $temp2;
    } elseif ((3 * $hue) < 2) {
        return ($temp1 + ($temp2 - $temp1) * ((2 / 3) - $hue) * 6);
    }
    return $temp1;
}

如果你有十进制RGB值(enhzflep展示了如何获得它们),你可以很容易地获得#ab01cd十六进制web字符串:

$rgb['r'] = ($t = round($rgb['r'] * 255, 0)) < 15 ? '0'.dechex($t) : dechex($t);
$rgb['g'] = ($t = round($rgb['g'] * 255, 0)) < 15 ? '0'.dechex($t) : dechex($t);
$rgb['b'] = ($t = round($rgb['b'] * 255, 0)) < 15 ? '0'.dechex($t) : dechex($t);
$hexweb = "#".$rgb['r'].$rgb['g'].$rgb['b'];