首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >RGB到HSL转换

RGB到HSL转换
EN

Stack Overflow用户
提问于 2016-08-24 08:45:25
回答 3查看 40K关注 0票数 65

我正在创建一个颜色选择工具和HSL滑块,我需要能够转换RGB到HSL。当我寻找一种方法来进行转换时,我发现了这个问题HSL到RGB颜色转换

虽然它提供了一个从RGB到HSL的转换函数,但我看不出计算中到底发生了什么。为了更好地理解它,我在维基百科上读过HSL和HSV

后来,我用"HSL和HSV“页面的计算重写了从"HSL到RGB颜色转换”的函数。

如果R是最大值,我就只能计算色调了。参见"HSL和HSV“页面中的计算:

这是来自另一个wiki页面的荷兰语:

这是从答案到"HSL到RGB颜色转换“:

代码语言:javascript
复制
case r: h = (g - b) / d + (g < b ? 6 : 0); break; // d = max-min = c

我用几个RGB值测试了这三个值,它们似乎产生了相似的(如果不是精确的)结果。我想知道的是他们的表演是一样的吗?对于某些特定的RGB值,我会得到不同的结果吗?我该用哪一种?

代码语言:javascript
复制
hue = (g - b) / c;                   // dutch wiki
hue = ((g - b) / c) % 6;             // eng wiki
hue = (g - b) / c + (g < b ? 6 : 0); // SO answer

代码语言:javascript
复制
function rgb2hsl(r, g, b) {
    // see https://en.wikipedia.org/wiki/HSL_and_HSV#Formal_derivation
    // convert r,g,b [0,255] range to [0,1]
    r = r / 255,
    g = g / 255,
    b = b / 255;
    // get the min and max of r,g,b
    var max = Math.max(r, g, b);
    var min = Math.min(r, g, b);
    // lightness is the average of the largest and smallest color components
    var lum = (max + min) / 2;
    var hue;
    var sat;
    if (max == min) { // no saturation
        hue = 0;
        sat = 0;
    } else {
        var c = max - min; // chroma
        // saturation is simply the chroma scaled to fill
        // the interval [0, 1] for every combination of hue and lightness
        sat = c / (1 - Math.abs(2 * lum - 1));
        switch(max) {
            case r:
                // hue = (g - b) / c;
                // hue = ((g - b) / c) % 6;
                // hue = (g - b) / c + (g < b ? 6 : 0);
                break;
            case g:
                hue = (b - r) / c + 2;
                break;
            case b:
                hue = (r - g) / c + 4;
                break;
        }
    }
    hue = Math.round(hue * 60); // °
    sat = Math.round(sat * 100); // %
    lum = Math.round(lum * 100); // %
    return [hue, sat, lum];
}
EN

回答 3

Stack Overflow用户

发布于 2019-10-17 06:44:07

此页为颜色空间之间的转换提供了一个函数,包括RGB到HSL。

代码语言:javascript
复制
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;

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

  // 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 + "%)";
}
票数 8
EN

Stack Overflow用户

发布于 2016-08-24 12:59:34

HSL中的色调就像圆圈中的一个角度。该角度的相关值位于0..360间隔内。然而,在计算中可能会产生负值。这就是为什么这三个公式是不同的。他们在最后也是这样做的,他们只是在0..360间隔之外处理不同的值。或者,准确地说,0..6间隔最终被乘以60到0..360

hue = (g - b) / c; // dutch wiki对负值不做任何操作,并假定后续代码可以处理负H值。

hue = ((g - b) / c) % 6; // eng wiki使用%运算符来拟合0..6间隔内的值

hue = (g - b) / c + (g < b ? 6 : 0); // SO answer通过添加+6使负值变为正值来处理负值

你看,这些只是表面上的不同。无论是第二个还是第三个公式,对你来说都很好。

票数 3
EN

Stack Overflow用户

发布于 2016-08-24 12:45:51

继续我的评论,英文版本看起来是正确的,但我不确定荷兰版本发生了什么,因为我不理解WIKI页面。

下面是我从英文WIKI页面制作的ES6版本,以及一些看起来与WIKI示例相匹配的示例数据(给出或接受Javascript的数字准确性)。希望在创建您自己的功能时,它可能会有用。

代码语言:javascript
复制
// see: https://en.wikipedia.org/wiki/RGB_color_model
// see: https://en.wikipedia.org/wiki/HSL_and_HSV

// expects R, G, B, Cmax and chroma to be in number interval [0, 1]
// returns undefined if chroma is 0, or a number interval [0, 360] degrees
function hue(R, G, B, Cmax, chroma) {
  let H;
  if (chroma === 0) {
    return H;
  }
  if (Cmax === R) {
    H = ((G - B) / chroma) % 6;
  } else if (Cmax === G) {
    H = ((B - R) / chroma) + 2;
  } else if (Cmax === B) {
    H = ((R - G) / chroma) + 4;
  }
  H *= 60;
  return H < 0 ? H + 360 : H;
}

// returns the average of the supplied number arguments
function average(...theArgs) {
  return theArgs.length ? theArgs.reduce((p, c) => p + c, 0) / theArgs.length : 0;
}

// expects R, G, B, Cmin, Cmax and chroma to be in number interval [0, 1]
// type is by default 'bi-hexcone' equation
// set 'luma601' or 'luma709' for alternatives
// see: https://en.wikipedia.org/wiki/Luma_(video)
// returns a number interval [0, 1]
function lightness(R, G, B, Cmin, Cmax, type = 'bi-hexcone') {
  if (type === 'luma601') {
    return (0.299 * R) + (0.587 * G) + (0.114 * B);
  }
  if (type === 'luma709') {
    return (0.2126 * R) + (0.7152 * G) + (0.0772 * B);
  }
  return average(Cmin, Cmax);
}

// expects L and chroma to be in number interval [0, 1]
// returns a number interval [0, 1]
function saturation(L, chroma) {
  return chroma === 0 ? 0 : chroma / (1 - Math.abs(2 * L - 1));
}

// returns the value to a fixed number of digits
function toFixed(value, digits) {
  return Number.isFinite(value) && Number.isFinite(digits) ? value.toFixed(digits) : value;
}

// expects R, G, and B to be in number interval [0, 1]
// returns a Map of H, S and L in the appropriate interval and digits
function RGB2HSL(R, G, B, fixed = true) {
  const Cmin = Math.min(R, G, B);
  const Cmax = Math.max(R, G, B);
  const chroma = Cmax - Cmin;
  // default 'bi-hexcone' equation
  const L = lightness(R, G, B, Cmin, Cmax);
  // H in degrees interval [0, 360]
  // L and S in interval [0, 1]
  return new Map([
    ['H', toFixed(hue(R, G, B, Cmax, chroma), fixed && 1)],
    ['S', toFixed(saturation(L, chroma), fixed && 3)],
    ['L', toFixed(L, fixed && 3)]
  ]);
}

// expects value to be number in interval [0, 255]
// returns normalised value as a number interval [0, 1]
function colourRange(value) {
  return value / 255;
};

// expects R, G, and B to be in number interval [0, 255]
function RGBdec2HSL(R, G, B) {
  return RGB2HSL(colourRange(R), colourRange(G), colourRange(B));
}

// converts a hexidecimal string into a decimal number
function hex2dec(value) {
  return parseInt(value, 16);
}

// slices a string into an array of paired characters
function pairSlicer(value) {
  return value.match(/../g);
}

// prepend '0's to the start of a string and make specific length
function prePad(value, count) {
  return ('0'.repeat(count) + value).slice(-count);
}

// format hex pair string from value
function hexPair(value) {
  return hex2dec(prePad(value, 2));
}

// expects R, G, and B to be hex string in interval ['00', 'FF']
// without a leading '#' character
function RGBhex2HSL(R, G, B) {
  return RGBdec2HSL(hexPair(R), hexPair(G), hexPair(B));
}

// expects RGB to be a hex string in interval ['000000', 'FFFFFF']
// with or without a leading '#' character
function RGBstr2HSL(RGB) {
  const hex = prePad(RGB.charAt(0) === '#' ? RGB.slice(1) : RGB, 6);
  return RGBhex2HSL(...pairSlicer(hex).slice(0, 3));
}

// expects value to be a Map object
function logIt(value) {
  console.log(value);
  document.getElementById('out').textContent += JSON.stringify([...value]) + '\n';
};

logIt(RGBstr2HSL('000000'));
logIt(RGBstr2HSL('#808080'));
logIt(RGB2HSL(0, 0, 0));
logIt(RGB2HSL(1, 1, 1));
logIt(RGBdec2HSL(0, 0, 0));
logIt(RGBdec2HSL(255, 255, 254));
logIt(RGBhex2HSL('BF', 'BF', '00'));
logIt(RGBstr2HSL('008000'));
logIt(RGBstr2HSL('80FFFF'));
logIt(RGBstr2HSL('8080FF'));
logIt(RGBstr2HSL('BF40BF'));
logIt(RGBstr2HSL('A0A424'));
logIt(RGBstr2HSL('411BEA'));
logIt(RGBstr2HSL('1EAC41'));
logIt(RGBstr2HSL('F0C80E'));
logIt(RGBstr2HSL('B430E5'));
logIt(RGBstr2HSL('ED7651'));
logIt(RGBstr2HSL('FEF888'));
logIt(RGBstr2HSL('19CB97'));
logIt(RGBstr2HSL('362698'));
logIt(RGBstr2HSL('7E7EB8'));
代码语言:javascript
复制
<pre id="out"></pre>

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39118528

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档