资讯专栏INFORMATION COLUMN

JavaScript中的颜色空间转换

tain335 / 1990人阅读

摘要:色相是这个圆柱的偏角,饱和度为圆柱水平切面的半径,亮度以圆柱的高度表示。这意味着中,饱和度总是从完全饱和变化到等价的灰色,而在中是从完全饱和变化为白色。到的换算在数学上定义为空间的坐标到空间的坐标的换算。当为亮度为极值时,饱和度恒等于。

我在做 webapp 的顶部导航栏时,碰到了一个挑战,导航栏的字体与图标要根据背景的颜色深浅来显示不同白色和黑色,但是导航栏的颜色是支持多种配色的,我不可能根据每一个配色去定义这个颜色的深浅,于是我开始研究起了颜色空间的转换……

对于CSS,我们最常见的就是 16进制的 RGB。 从黑色到白色依次是 #000000 ... #FFFFFF

RGB

RGB也称三原色光模式(RGB color mode),原理是将红绿蓝三种颜色的色光已不同的比例相加,已形成多种多样的色光(三原色不可能用其他灯光的颜色合成) -- 维基百科

当前计算机硬件采取每一个像素用24Bit来表示不同的颜色,每8位表示一个原色的强度,最高值为2^8,也就是256个值,组合起来可以表示 16777216(256^3) 种颜色。之所以24位,这是因为人眼最高只能分辨出1000万种颜色,因此足矣。

当然也有32Bit的模式,但是实际上也是24Bit,余下的8Bit不分配到像素中,主要是为了提高数据输送的速度(一般而言1word为16Bit,32Bit === 1 double word,处理器不需要做多余的换算),同样在一些特殊情况下,余下的8Bit 用来表示像素的透明度

因此 #FFFFFF 同样可以表示为 rgb(255, 255, 255)

很自然地就可以采用三维空间来描述RGB的全值域,如图所示x轴为红色,y轴为蓝色,z轴为绿色。黑色藏在了立方体的背面。MAX=255,MIN=0。用这种方式表示可以很简单得通过计算两个点的距离远近来判断颜色是否相近。

几个极点的坐标分别表示的颜色

r g b name
0 0 0 黑(black)
255 255 255 白(white)
255 0 0 红(red)
255 255 0 黄(yellow)
0 255 0 绿(green)
0 255 255 青(cyan-blue)
0 0 255 蓝(blue)
255 0 255 品红(magenta)

但是这不足以解决文章开头需要解决的问题,因为从当前的颜色空间中,我们无法要直观地去辨别那些颜色是亮色,哪些颜色是暗色,很难,我们只知道一个颜色的红绿蓝混合比例。我们需要找出一种规律,去分类颜色的明暗。

HSL/HSV

出于我们感性的角度,颜色混合并不直观,我们判别一种颜色的思维首先会看看这是什么颜色,然后再确认颜色深浅如何、明暗度如何。事实上大部分艺术家在创作的时候也更倾向于这种思维。

所以我们很多软件上的调色工具都会基于这样的思路去设计,首先会有一个色板、然后会有饱和度、亮度这样的调整。

然而一早出现HSL的时候却不是为了此目的,记录最早显示的是1938年 Georges Valensi 为了解决彩色电视信号兼容单色电视信号的问题发明了HSL色彩空间(单色电视信号仅包括L信号)。往后1978年 Alvy Ray Smith 在编写 SuperPaint (SuperPaint是第一个计算机光栅图形编辑器或“绘画”程序之一)的时候发明了HSV(HSB)模型。经过实践反映了这两种模型给使用者可以带来更直观的感受。

HSL 三种维度分辨为 Hue(色相)、Saturation(饱和度)、Lightness(亮度),几何表示为一个圆柱坐标系。色相是这个圆柱的偏角,饱和度为圆柱水平切面的半径,亮度以圆柱的高度表示。

HSV 的三种维度分别是 Hue(色相)、Saturation(饱和度)、Value(明度),在色相的定义上与HSL保持一致,但是在饱和度上的定义是有区别的。

这两者之间应该使用哪种模型,目前是非常有争议的。支持HSL的人认为他更好的反映了饱和度和亮度作为两个独立参数的概念直觉(HSV最低的饱和度为白色是非常反直觉的),而另一部分的人认为,HSL的饱和度的定义容易给人造成迷惑,比如亮度极高时,白色被认为是高饱和度的。这意味着

HSL中,饱和度总是从完全饱和变化到等价的灰色,而在HSV中是从完全饱和变化为白色。

HSL中亮度的变化跨域从黑色到选择的色相再到白色的过程,HSV中明度的定义只从黑色过渡到选择的色相。

因此通常在绘制坐标时,饱和度会被替换为 色度(Chroma)表示,用以过滤一些不符合直觉的坐标,HSL 对应呈双锥型的 HCL,而HSV 则对应锥形的 HCV 模型。

如今HSL 与 HSV 在软件上已经有了大量的运用。比如

Adobe 套件(Photoshop,Illustrator...)-- HSV

APPLE Mac OS X 系统颜色选择器 --HSV

CSS3 -- HSL

Windows系统颜色选择器 -- HSL

当然随着系统的升级与支持,也不乏有两者都支持的软件。

没有孰优孰劣,在做选择的时候只考虑使用的场景哪种更适合,当然在WEB的范畴中,由于CSS3的标准规定,HSL更有利于颜色的换算。而了解到这里,我已经对开头的需求有了一个明确的实现方案。

RGB 到 HSL 的换算

在数学上定义为 RGB 空间的r,g,b坐标到 HSL 空间的 h,s,l 坐标的换算。

r,g,b ∈ [0, 1] ,max = max(r, g, b), min = min(r, g, b)

h ∈ [0, 360], s,l ∈ [0, 1]

首先会先计算色相值,对应是在圆柱横切面角度六等分的不同夹角下的值有不同的换算公式,从上往下1到5对应的区域

实际上 max = min 时是的灰色,h = undefined,上图中的第一条公式表示有误。当 h = 0° 一般表示计算为红色,也就是包含在了第二条计算公式中。这点需要特别注意

其次是亮度的计算,其实亮度的定义这方面是有争议的,并不是真正意义上明确的,而是基于不同的模型做不同的定义,这里就不做具体的讨论,HSL中亮度的定义取RGB中最大值与最小值相加的二分之一

最后是饱和度的计算公式,首先定义色度Chroma = max - min,从生理角度理解三种视锥细胞中,刺激最大与刺激最小之间的差异,
让人产生了颜色的鲜艳感,而与刺激中等的细胞关系不大。

我们一开始介绍HSL的时候有提到过,HSL模型中有些值实际上已经超出了RGB定义的范畴,超出的部分实际上是没有意义的,所以圈定了另外一个范围为 HCL,是呈现一种双锥形的几何表示。当为亮度为极值时,饱和度恒等于0。
中间分成两节对应不同的计算公式。下半截对应公式2,上半截对应公式1,紧接着饱和度是受到亮度的制约,因此配合亮度进行计算。

代码实现
function RGB2HSL(r, g, b) {
  r = r / 255
  g = g / 255
  b = b / 255

  const max = Math.max(r, g, b)
  const min = Math.min(r, g, b)
  const delta = max - min
  let h, s, l

  if (max ==== min) {
    h = 0
  } else if (max === r) {
    h = ((g - b) / delta) % 6
  } else if (max === g) {
    h = (b - r) / delta + 2
  } else {
    h = (r - g) / delta + 4
  }
  h = Math.round(h * 60)
  if (h < 0) h += 360

  l = (max + min) / 2,
  s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));

  // 切换为百分比模式
  s = +(s * 100).toFixed(1);
  l = +(l * 100).toFixed(1);

  return { h, s, l }
}
RGB 到 HSV(HSB)

色相的换算跟HSL是一致的。饱和度和明度定义分别是

代码实现
function RGB2HSV(r, g, b) {
  r = r / 255
  g = g / 255
  b = b / 255

  const max = Math.max(r, g, b)
  const min = Math.min(r, g, b)
  const delta = max - min
  let h, s, l

  if (max ==== min) {
    h = 0
  } else if (max === r) {
    h = ((g - b) / delta) % 6
  } else if (max === g) {
    h = (b - r) / delta + 2
  } else {
    h = (r - g) / delta + 4
  }
  h = Math.round(h * 60)
  if (h < 0) h += 360

  // 基于HSL函数简单的变化即可适用
  l = max,
  s = delta === 0 ? 0 : delta / max;

  s = +(s * 100).toFixed(1);
  l = +(l * 100).toFixed(1);

  return { h, s, l }
}
HSL 到 RGB

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/105452.html

相关文章

  • JavaScript检查颜色冲突

    摘要:也许这些是预先制作的配色方案,公司颜色或从图像中提取。第二个等式称为,因为它将颜色空间转换为,这考虑了其组成部分的不同影响。根据该值的位置,我们将返回相应的最高对比色。红色和粉红色值显示白色文本而不是黑色。 注意:本文非原创,而是翻译国外大师文章,布赖恩苏达。布赖恩苏达是一位信息大师,每天都在努力使网络变得更加美好。自从90年代中期发现互联网以来,Brian Suda已经花了很多时间与...

    littlelightss 评论0 收藏0
  • 前端入门23-CSS预处理器(Less&Sass)

    摘要:声明声明本篇内容梳理自以下几个来源网站的文档中文网感谢大佬们的分享。这个时候,预处理器就出现了,其实应该是说和这类语言出现了。声明 本篇内容梳理自以下几个来源: Github:smyhvae/web Bootstrap网站的 less 文档 Sass中文网 感谢大佬们的分享。 正文-CSS预处理(less&Sass) CSS预处理 什么是 CSS 预处理?为什么要有 CSS 预处理? 这...

    freecode 评论0 收藏0
  • WebRender:让网页渲染如丝顺滑

    摘要:让网页渲染如丝顺滑本文转载自众成翻译译者文蔺链接原文发布在即。另一部分是绘制与合成,这正是渲染器的工作。两帧之间的时间被称为帧预算。因此要确保在显示器再次检查前将所有像素放入帧缓冲区。将页面分成图层,拓展了最佳情形数量。 WebRender:让网页渲染如丝顺滑 本文转载自:众成翻译译者:文蔺链接:http://www.zcfy.cc/article/4386原文:https://hac...

    blair 评论0 收藏0
  • WebRender:让网页渲染如丝顺滑

    摘要:让网页渲染如丝顺滑本文转载自众成翻译译者文蔺链接原文发布在即。另一部分是绘制与合成,这正是渲染器的工作。两帧之间的时间被称为帧预算。因此要确保在显示器再次检查前将所有像素放入帧缓冲区。将页面分成图层,拓展了最佳情形数量。 WebRender:让网页渲染如丝顺滑 本文转载自:众成翻译译者:文蔺链接:http://www.zcfy.cc/article/4386原文:https://hac...

    zorro 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<