1. 引言
本节不是规范性的。
测试
本节不是规范性的,不需要测试。
本模块描述了 CSS 属性,这些属性允许作者指定元素文本内容的前景颜色和不透明度。 本模块还详细描述了 CSS <color> 值类型。
它不仅定义了已经存在于 CSS1、CSS2 和 CSS Color 3 中的与颜色相关的属性和值, 还定义了新的属性和值。
特别是,它允许在 sRGB 之外的其他 颜色空间 中指定颜色; 以前,即使显示设备支持,更饱和的颜色(超出 sRGB 色域的颜色)也不能在 CSS 中使用。
可用的 草稿实现报告 已发布。
1.1. 值的定义
本规范遵循 CSS属性定义约定(见 [CSS2]), 并使用 值定义语法(见 [CSS-VALUES-3])。 未在本规范中定义的值类型,参见 CSS数值与单位 [CSS-VALUES-3]。 与其它CSS模块的结合可能会扩展这些值类型的定义。
除了在定义中列出的特定于属性的值外,本规范中定义的所有属性还接受 CSS-wide 关键字 作为其属性值。 为了可读性,它们没有被显式重复。
2. 颜色术语
测试
本节提供后续使用的定义,不需要测试。
颜色 是对人类视觉对光线或被光照亮的物体的视觉感知的定义(数值或文本)。 对人类颜色感知的客观研究称为 颜色测量学。
物体的颜色取决于它在每个可见波长上反射的光的多少, 以及照亮它的光的实际颜色(同样是每个波长上的光量)。 它通过分光光度计进行测量。
发光物体的颜色(包括计算机屏幕上的颜色) 取决于它在每个可见波长上发射的光的多少。 它通过分光辐射计进行测量。
如果两个物体具有不同的光谱,但仍然产生相同的物理感觉, 我们说它们具有相同的颜色。 我们可以通过将光谱转换为 CIE XYZ(三个数字)来计算两个颜色是否相同。
颜色空间 是对颜色相对于基础颜色测量学模型的组织, 使得在该颜色空间中任何颜色都有明确的、可客观测量的含义。 这也意味着相同的颜色可以在多个颜色空间中表示, 或从一个颜色空间转换为另一个颜色空间, 同时看起来保持相同。
使用分光光度计对叶子进行测量,发现颜色为 lch(51.2345% 21.2 130), 也就是 lab(51.2345% -13.6271 16.2401)。
同样的颜色可以在不同的颜色空间中表示:
color ( sRGB0.41587 0.503670 0.36664 ); color ( display-p30.43313 0.50108 0.37950 ); color ( a98-rgb0.44091 0.49971 0.37408 ); color ( prophoto-rgb0.36589 0.41717 0.31333 ); color ( rec20200.42210 0.47580 0.35605 );
加色色彩空间意味着坐标系在光强度上是线性的。 CIE XYZ 色彩空间是一种加色色彩空间。 XYZ 的 Y 分量是亮度,即单位面积的光强度, 或者说“它有多亮”。亮度以坎德拉每平方米 (cd/m²) 为单位进行测量, 也称为尼特。
在加色色彩空间中,可以进行计算 以准确预测颜色混合。大多数 RGB 空间 不是加色的,因为其分量是伽马编码的。取消这种伽马编码 会产生线性光值。
如果我们有两种不同颜色的聚光灯照射在舞台上, 一种测得的值是 color(xyz 0.15 0.24 0.17), 另一种是 color(xyz 0.11 0.06 0.06), 那么我们可以准确预测,如果两个有色光束重叠, 混合后的颜色将是 XYZ 分量值的总和,即 color(xyz 0.26 0.30 0.23)。
色度 是将亮度分量排除后的颜色测量值。 从上述相同的灯光示例中,一个灯光的u',v' 色度为 (0.2537, 0.5268), 而两个灯光的色度相同(它们的颜色相同,只是更亮)。
色度是加性的, 因此它可以准确预测混合物的色度(但不能预测其产生的亮度)。 色度是二维的,因此可以在色度图上轻松表示,以预测颜色混合的色度。 任何两个颜色都可以混合,结果颜色将位于图上连接它们的直线上。 三种颜色形成一个平面,结果颜色将位于它们在图上形成的三角形内。
因此,一旦线性化,RGB 色彩空间就是加色的, 它们的色域由红、绿、蓝三原色的色度, 加上白点(所有三种原色都达到最大强度时形成的颜色)的色度定义。
大多数色彩空间使用少数几种模拟日光的白点之一, 这些白点以相应黑体辐射器的相关色温 (CCT) [Understanding_CCT] 命名。 例如,D65 是一个日光白点, 对应于 6500 开尔文的相关色温 (实际上是 6504, 因为自从最初定义该颜色以来,普朗克常数的值已发生变化)。
为了避免累积的往返误差, 在计算的所有地方必须一致地使用相同的色度值, 这样才能实现最大兼容性。 因此,在本规范中定义了以下两个标准的白昼模拟白点:
| 名称 | x | y | CCT |
|---|---|---|---|
| D50 | 0.345700 | 0.358500 | 5003K |
| D65 | 0.312700 | 0.329000 | 6504K |
当已知颜色空间或产生颜色的设备的物理特性 (例如其使用的基色的色度, 或对一组给定输入的响应所产生的颜色)时, 我们称它为已表征。
如果另外进行了调整,使设备满足校准目标,例如白点、中性色、调响应的可预测性和一致性, 那么它被称为已校准。
目前的物理设备还无法产生人眼可以看到的每种可能颜色。 给定设备能够产生的颜色范围称为色域 (不要与伽马混淆)。 色域有限的设备无法产生非常饱和的颜色, 如彩虹中的颜色。
不同颜色空间的色域可以通过查看可以表示的颜色体积(以立方 Lab 单位为单位)来比较。 下表检查了 CSS 中可用的预定义颜色空间。
| 颜色空间 | 体积(百万 Lab 单位) |
|---|---|
| sRGB | 0.820 |
| display-p3 | 1.233 |
| a98-rgb | 1.310 |
| prophoto-rgb | 2.896 |
| rec2020 | 2.042 |
CSS 中的颜色要么是无效颜色, 如下所述的每种语法形式, 要么是有效颜色。
任何不是无效颜色的颜色都是有效颜色。
某些颜色可以是有效颜色, 但仍然超出输出设备(屏幕、投影仪或打印机)可以产生的颜色范围。
这种情况称为超出色域。
每个有效颜色要么对于特定输出设备(屏幕或打印机)在色域内, 要么超出色域。
因为,在 display-p3 中表示时, 一个或多个坐标要么大于 1.0,要么小于 0.0:color ( prophoto-rgb0.88 0.45 0.10 )
此颜色是有效的, 例如,它可以用作渐变的停止点, 但需要进行CSS 色域映射, 以便显示,从而产生看起来相似但色度较低(饱和度较低)的颜色。color ( display-p31.0844 0.43 0.1 )
3. 在 CSS 中应用颜色
3.1. 可访问性与通过颜色传达信息
测试
本节提供编写指导,不需要测试。
虽然颜色可以为文档增加重要信息并提高其可读性, 但颜色本身不应成为传达重要信息的唯一手段。 作者在文档中使用颜色时应考虑 W3C Web 内容可访问性指南 [WCAG21]。
1.4.1 使用颜色: 颜色不能作为传达信息、指示操作、提示响应或区分视觉元素的唯一视觉手段
3.2. 前景颜色:color 属性
| 名称: | color |
|---|---|
| 值: | <color> |
| 初始值: | CanvasText |
| 适用于: | 所有元素和文本 |
| 继承: | 是 |
| 百分比: | N/A |
| 计算值: | 计算的颜色,参见解析颜色值 |
| 规范顺序: | 按语法 |
| 动画类型: | 按计算值类型 |
测试
此属性指定元素的主要前景颜色。 它用作文本内容的填充颜色, 并且还指定了 使用的值,currentcolor 将解析为该值, 这允许间接引用该前景颜色,并影响其他各种颜色属性的初始值, 如 border-color 和 text-emphasis-color。
- <color>
- 将主要前景颜色设置为指定的 <color>。
em{ color : lime; } /* color keyword */ em{ color : rgb ( 0 255 0 ); } /* RGB range 0-255 */ em{ color : rgb ( 0 % 100 % 0 % ); } /* RGB range 0%-100% */ em{ color : color ( sRGB0 1 0 ); } /* sRGB range 0.0-1.0 */
当应用于文本时,此属性(包括其 alpha 分量)对“彩色字形”没有影响(例如某些字体中的表情符号),这些字形由内置调色板着色。
然而,一些彩色字体能够引用上下文“前景色”,
例如 OpenType 的 COLR 表中的调色板条目 0xFFFF,
或者 SVG-in-OpenType 中的 context-fill 值。
在这种情况下,前景色由此属性设置,与设置 currentcolor 值相同。
3.3. 透明度:opacity 属性
透明度可以被看作是后处理操作。 从概念上讲,在元素(包括其子元素)被渲染为 RGBA 离屏图像后, 透明度设置指定如何将离屏渲染与当前的合成渲染进行混合。 详情见简单的 alpha 合成。
| 名称: | opacity |
|---|---|
| 值: | <opacity-value> |
| 初始值: | 1 |
| 适用于: | 所有元素 |
| 继承: | 否 |
| 百分比: | 映射到范围 [0,1] |
| 计算值: | 指定的数值,限定在 [0,1] 范围内 |
| 规范顺序: | 按语法 |
| 动画类型: | 按计算值类型 |
测试
- clip-opacity-out-of-flow.html (实时测试) (来源)
- t32-opacity-basic-0.0-a.xht (实时测试) (来源)
- t32-opacity-basic-0.6-a.xht (实时测试) (来源)
- t32-opacity-basic-1.0-a.xht (实时测试) (来源)
- t32-opacity-clamping-0.0-b.xht (实时测试) (来源)
- t32-opacity-clamping-1.0-b.xht (实时测试) (来源)
- t32-opacity-offscreen-b.xht (实时测试) (来源)
- t32-opacity-offscreen-multiple-boxes-1-c.xht (实时测试) (来源)
- t32-opacity-offscreen-multiple-boxes-2-c.xht (实时测试) (来源)
- t32-opacity-offscreen-with-alpha-c.xht (实时测试) (来源)
- t32-opacity-zorder-c.xht (实时测试) (来源)
- opacity-computed.html (实时测试) (来源)
- opacity-valid.html (实时测试) (来源)
- opacity-invalid.html (实时测试) (来源)
- composited-filters-under-opacity.html (实时测试) (来源)
- filters-under-will-change-opacity.html (实时测试) (来源)
- color-composition.html (实时测试) (来源)
- opacity-interpolation.html (实时测试) (来源)
- canvas-change-opacity.html (实时测试) (来源)
- opacity-animation-ending-correctly-001.html (实时测试) (来源)
- opacity-animation-ending-correctly-002.html (实时测试) (来源)
- <不透明度值>
-
应用于元素的透明度。
最终的透明度应用于整个元素,而不是某个特定颜色。
透明度值超出范围 [0,1] 并非无效,且在指定值中会被保留, 但在计算值中会被限定在 [0, 1] 范围内。
CSS 中的不透明度使用 <opacity-value> 语法表示, 例如在 opacity 属性中。
<opacity-value> = <number> | <percentage>
表示为 <number> 时, 其值的有效范围是 0(表示完全透明) 到 1(表示完全不透明)。 它也可以写成 <percentage>, 其计算结果为等效的 <number>(0% 对应 0,100% 对应 1)。
opacity 属性将指定的不透明度应用于元素 整体, 包括其内容,而不是单独应用于每个子元素。 这意味着,例如,即使 opacity 小于 1, 遮挡部分背景的不透明子元素仍然会继续遮挡,但元素和子元素作为一个整体将显示底层页面。
这也意味着, 元素中所有字符的字形都被 作为一个整体 处理; 任何重叠部分都不会增加不透明度。
如果希望每个字形具有不同的不透明度, 可以通过使用包含 alpha 通道的颜色值来实现, 而不是设置 opacity 属性。
如果一个盒子具有小于 1 的 opacity, 它会为其子元素形成一个堆叠上下文。 (这可以防止其内容在 z 轴上与外部内容交错。)
此外,如果 z-index 属性应用于盒子, 则该元素的 auto 值被视为 0; 否则它会在其父堆叠上下文中与具有堆叠级别 0 的定位元素在同一层上绘制 (就好像它是具有 z-index:0 的定位元素一样)。
更多关于堆叠上下文的信息,请参阅 第 9.9 节 和 附录 E,参见 [CSS2]。
这些关于堆叠顺序的规则不适用于 SVG 元素, 因为 SVG 有其自己的 渲染模型([SVG11],第 3 章)。
opacity 属性的值 不会影响命中测试。
3.4. 标记图像的颜色空间
标记图像是指显式分配了颜色配置文件的图像, 由图像格式定义。 这通常通过包含国际色彩联盟(ICC)配置文件来完成 [ICC]。
例如,JPEG [JPEG]、PNG [PNG] 和 TIFF [TIFF] 都指定了一种嵌入 ICC 配置文件的方法。
图像格式也可以使用其他等效的方法,通常是为了简洁。
例如,PNG 指定了一种方法(sRGB 块), 用于显式地将图像标记为 sRGB 颜色空间, 而无需包含 sRGB ICC 配置文件。
类似地,PNG 指定了一种紧凑的方法 (cICP 块), 用于显式地将图像标记为各种 SDR 或 HDR 颜色空间之一, 例如 Display P3 或 BT.2100 HLG, 而无需包含 ICC 配置文件。
标记的 RGB 图像, 以及使用 RGB 转换(如 YCbCr)的标记图像, 如果颜色配置文件或其他识别信息有效, 必须被视为处于指定的颜色空间中。
测试
例如,当运行在具有 Display P3 显示器的系统上的浏览器显示一个标记为 ITU Rec BT.2020 [Rec.2020] 色彩空间的 JPEG 图像时,必须将颜色从 ITU Rec BT.2020 转换为 Display P3,以便正确显示。不能将 ITU Rec BT.2020 值视为 Display P3 值,否则会产生错误的颜色。
如果颜色配置文件或其他标识信息无效,则图像将按照未标记图像的描述进行处理。
3.5. 未标记颜色的色彩空间
为了兼容性,HTML 中指定的颜色 以及未标记图像必须被视为 处于 sRGB 色彩空间([SRGB]) 除非另有规定。
未标记图像是指未根据图像格式定义明确指定颜色配置文件的图像。
此规则不适用于未标记视频,因为未标记视频应假定采用 ITU 定义的色彩空间。
-
低于 720p 时,使用 Recommendation ITU-R BT.601 [ITU-R-BT.601]
-
720p 时,使用 SMPTE ST 296(与 709 具有相同的色彩学) [SMPTE296]
-
1080p 时,使用 Recommendation ITU-R BT.709 [ITU-R-BT.709]
-
4k (UHDTV) 及更高分辨率时,使用 ITU-R BT.2020 [Rec.2020] 用于 SDR 视频
4. 表示颜色: <color> 类型
测试
本节描述了一种类型,主要在使用该类型的地方进行测试。
CSS 中的颜色表示为颜色分量列表, 有时也称为“通道”, 代表色彩空间中的轴。 每个分量都有一个最小值和最大值, 并且可以取这两个值之间的任何值。 此外,每种颜色都附带一个 Alpha 分量, 指示其透明程度, 从而指示可以通过颜色看到多少背景。
CSS 有多种指定颜色值的语法:
-
sRGB 十六进制颜色表示法,它以十六进制表示法表示 RGB 和 Alpha 分量
-
各种颜色函数,可以使用各种色彩空间和坐标系表示颜色
-
常量命名颜色关键字
-
变量<system-color>关键字和currentColor关键字。
颜色函数使用 CSS 函数表示法通过指定其分量坐标来表示各种色彩空间中的颜色。 其中一些使用圆柱极坐标颜色模型, 通过<hue>角度、代表亮度 (黑到白)的中心轴, 以及代表饱和度或色度 (颜色与中性灰色的距离)的半径来指定颜色。 其他的则使用矩形正交颜色模型, 使用三个正交分量轴指定颜色。
第 4 级中可用的颜色函数有:
-
rgb() 及其 rgba() 别名, 与 十六进制颜色表示法类似,直接通过红/绿/蓝/透明度分量指定 sRGB 颜色。
-
lab(), 按照 CIE LAB 矩形坐标模型, 通过 CIE 明度和 a、b 轴色调坐标(红/绿与黄/蓝倾向性)指定 CIELAB 颜色。
-
lch(), 按照 CIE LCH 圆柱坐标模型 通过 CIE 明度、色度和色调指定 CIELAB 颜色。
-
oklab(), 按照 Oklab 矩形坐标模型, 通过 Oklab 明度和 a、b 轴色调坐标(红/绿与黄/蓝倾向性)指定 Oklab 颜色。
-
color(), 允许在多种色彩空间中指定颜色, 包括 sRGB、 线性光 sRGB、 Display P3、 线性光 Display P3、 A98 RGB、 ProPhoto RGB、 ITU-R BT.2020-2, 以及 CIE XYZ。
为便于其他规范引用, 不透明黑色 定义为 颜色 rgb(0 0 0 / 100%); 透明黑色 为同样的颜色, 但完全透明,即 rgb(0 0 0 / 0%)。
测试
4.1. <color> 语法
测试
本节提供稍后使用的定义,不需要测试。
在 CSS 中,颜色由 <color> 类型表示:
<color> = <color-base> | currentColor | <system-color> <color-base> = <hex-color> | <color-function> | <named-color> | transparent <color-function> = <rgb () > | <rgba () > | <hsl () > | <hsla () > | <hwb () > | <lab () > | <lch () > | <oklab () > | <oklch () > | <color () >
绝对颜色是一个 <color>,其计算值具有绝对的、色度学解释。这意味着该值不是:
-
currentColor(它取决于 color 属性的值)
-
一个 <system-color>(它取决于颜色模式)
解析为 sRGB 的颜色有:
支持旧版颜色语法的函数有:
<hsl()>、<hsla()>、<hwb()>、<lch()> 和 <oklch()> 颜色函数是使用 <hue> 角度的圆柱极坐标颜色表示法; 其他颜色函数使用矩形正交颜色表示法。
4.1.1. 现代(空格分隔)颜色函数语法
本规范中首次定义的所有绝对颜色函数形式 都使用现代颜色语法, 这意味着:
-
颜色分量由空格分隔
-
可选的 alpha 项由斜杠 (“/”) 分隔
-
序列化时的最低所需精度已定义, 并且可能大于每个分量 8 位
-
使用 <percentage> 和 <number> 的分量可以自由混合
4.1.2. 传统(逗号分隔)颜色函数语法
为了 Web 兼容性, rgb()、rgba()、hsl() 和 hsla() 的语法形式 (在早期规范中定义的那些) 也支持一种旧版颜色语法,它具有以下区别:
-
颜色分量用逗号分隔 (可选地在前面和/或后面加上空格)
-
非不透明形式使用单独的表示法 (例如 hsla() 而不是 hsl()) 并且 alpha 项用逗号分隔 (可选地在前面和/或后面加上空格)
-
最低所需精度较低,每个分量 8 位
-
不允许使用 none 值
-
颜色分量必须全部使用 <percentage> 或全部使用 <number> 来指定,它们不能混合使用。
对于本级别或随后的级别中引入的 颜色函数,在没有 Web 兼容性问题的情况下,传统颜色语法是无效的。
4.2. 表示颜色中的透明度:<alpha-value> 语法
测试
本节提供后续使用的定义,不需要测试。
<alpha-value> = <number> | <percentage>
注: 此语法按原样用于传统颜色 语法。 对于现代颜色 语法, 此语法进行了扩展, 使 alpha 分量 也可以采用值 none。
除非另有规定,颜色的 <alpha-value> 组件在省略时默认为 100%。超出范围 [0,1] 的值不是无效的,但在解析值时会被限制在该范围内。
4.3. 表示圆柱坐标色相:<hue> 语法
测试
本节提供后续使用的定义,不需要测试。
色相表示为颜色圆的一个角度(将彩虹扭曲成一个圆,并在紫色和红色之间加入紫红色)。
<hue> = <number> | <angle>
由于这个值通常以度数给出,参数也可以用数字表示,这个数字被解释为度数,是 标准单位。
这个数字被标准化为范围 [0,360)。
在 hsl(360 0 0) 中,<hue> 分量被标准化为 0 度。
在 hsl(calc(-infinity) 0 0) 或 hsl(calc(infinity) 0 0) 中, <hue> 分量再次被标准化为 0 度。
注意: 特定色相对应的角度和间距取决于色彩空间。例如,在使用 sRGB 色彩空间的 HSL 和 HWB 中,sRGB 的绿色是 120 度。在 LCH 中,sRGB 绿色是 134.39 度,display-p3 绿色是 136.01 度,a98-rgb 绿色是 145.97 度,prophoto-rgb 绿色是 141.04 度(因为这些都是不同的绿色阴影)。
<hue> 分量是最常变为无效的颜色分量; 任何足够接近中心非彩色轴的颜色 都将具有无效的色相分量。
4.4. “缺失”的颜色组件和 none 关键字
在某些情况下, 一种颜色可以有一个或多个 缺失的 颜色分量。
在本规范中, 对于某些颜色(例如 white),这会由于基于色相的插值 而自动发生; 其他规范可以定义 分量会自动缺失的其他情况。
也可以显式指定这种情况, 方法是在颜色函数中为某个分量提供关键字 none。 所有颜色函数 (使用传统颜色语法的函数除外) 都允许将其任意分量指定为 none。
这样做时应谨慎, 并且仅在需要由此产生的特定效果时才这样做。
测试
- none-components-treated-as-zero.html (live test) (source)
- color-computed-color-function.html (live test) (source)
- color-computed-hsl.html (live test) (source)
- color-computed-hwb.html (live test) (source)
- color-computed-lab.html (live test) (source)
- color-computed-none.html (live test) (source)
- color-computed-powerless.html (live test) (source)
- color-computed-relative-color.html (live test) (source)
- color-computed-rgb.html (live test) (source)
- color-invalid-hsl.html (live test) (source)
- color-invalid-rgb.html (live test) (source)
- color-valid-color-function.html (live test) (source)
- color-valid-color-mix-function.html (live test) (source)
- color-valid-hsl.html (live test) (source)
- color-valid-hwb.html (live test) (source)
- color-valid-lab.html (live test) (source)
- color-valid-relative-color.html (live test) (source)
- color-valid-rgb.html (live test) (source)
有关在颜色插值等组合两种颜色的情况下处理缺失分量的信息, 请参阅 § 13.3 使用缺失分量进行插值。
对于其他所有用途,缺失组件表现为零值,采用该组件的适当单位:0,0%,或0deg。这包括直接渲染颜色、将其转换为其他颜色空间、对颜色组件值执行计算等。
如果带有缺失组件的颜色被序列化或直接呈现给作者,对于传统颜色语法,其表示该组件为零值;否则,则表示该组件为none关键字。
结果将是一个真正看起来像绿色和白色混合的颜色,而不是看起来略带红色(如果将白色的色相默认为0deg)。
例如,要将一种颜色动画化为“灰度”,无论该颜色是什么,可以将其与oklch(none 0 none)进行插值。这将从起始颜色中获取色相和亮度,但将其色度动画化为0,使其在整个动画中保持均匀亮度的灰色。
手动执行此操作将需要显式匹配起始颜色的色相和亮度。
4.4.1. “无效”颜色组件
个别颜色语法可以指定,在某些情况下,它们的语法中的某个组件会成为无效颜色组件。这表明该组件的值不会影响渲染的颜色;无论你赋予它什么值,屏幕上显示的颜色都是相同的。
例如,在hsl()中,当饱和度组件为0%时,色相组件是无效的;0%的饱和度表示灰度颜色,它根本没有色相,因此0deg和180deg或任何其他角度,都会得到完全相同的结果。
如果手动指定了无效分量, 则其行为正常; 其无效的事实没有任何影响。
但是,如果颜色是通过色彩空间转换自动生成的, 则结果中的任何无效分量都必须设置为缺失, 而不是转换过程中产生的任何值。
当执行颜色空间转换至圆柱极坐标颜色空间时, 用户代理应将色相分量视为无作用, 如果色度(或其他衡量色彩鲜艳程度的度量,例如 hsl 中的饱和度) 小于或等于为该颜色空间指定的 epsilon(ε)。 例如,转换为 oklch() 的灰色可能会 由于数值误差, 具有极小的色度,而不是精确的 0%; 因此,色相分量是无作用的。
当将无效的色相分量改为缺失分量时, 色度(或其他色彩度量,例如 hsl 中的饱和度;参见类似分量)会被设为零, 以避免放大浮点噪声。 负值不会被钳制为零, 只有当该值大于零但小于或等于 ε 时,才会将其设为零。 对于 hwb,则改为遵循 以下步骤:
1 . 如果 W + B 大于或等于 ε 且 W + B 小于100 1.1 . 如果 W 和 B 均不缺失1.1.1 . 将 B 设为100 - W1.2 . 否则,如果 W 不缺失1.2.1 . 将 W 设为100 1.3 . 否则,如果 B 不缺失1.3.1 . 将 B 设为100
4.5. 解析 <color> 值
测试
本节提供了一个在其他地方引用的定义,不需要测试。
注意:此算法并非旨在解析 CSS 样式表或 CSSOM 接口中指定的 CSS <color> 值,而是在 HTML 属性或 Canvas 接口等其他地方使用。
5. sRGB 颜色
CSS 中位于 sRGB 色彩空间内的颜色通过三元值来表示——红、绿和蓝——标识 sRGB 色彩空间中的一个点 [SRGB]。 这是一种国际公认的设备无关的色彩空间,因此适用于指定在计算机屏幕上显示的颜色,也适用于在其他类型的设备(如打印机)上指定颜色。
CSS 也允许使用非 sRGB 的 色彩空间,如 第 10 节 预定义的色彩空间 中所述。
CSS 提供了几种直接指定 sRGB 颜色的方法:十六进制颜色、rgb()/rgba() 颜色函数、hsl()/hsla() 颜色函数、hwb() 颜色函数、命名颜色, 以及 transparent 关键字。
5.1. RGB 函数:rgb() 和 rgba()
rgb() 和 rgba() 函数通过直接指定 r、g 和 b(红、绿、蓝)分量来定义 sRGB 颜色。 它们的语法是:
rgb () =[ <legacy-rgb-syntax> | <modern-rgb-syntax>] rgba () =[ <legacy-rgba-syntax> | <modern-rgba-syntax>] <legacy-rgb-syntax> =rgb ( <percentage>#{ 3 } , <alpha-value>?) |rgb ( <number>#{ 3 } , <alpha-value>?) <legacy-rgba-syntax> =rgba ( <percentage>#{ 3 } , <alpha-value>?) |rgba ( <number>#{ 3 } , <alpha-value>?) <modern-rgb-syntax> =rgb ( [ <number> | <percentage> | none] { 3 } [ /[ <alpha-value> | none] ] ?) <modern-rgba-syntax> =rgba ( [ <number> | <percentage> | none] { 3 } [ /[ <alpha-value> | none] ] ?)
| 百分比 | 适用于 r、g 和 b |
|---|---|
| 百分比参考范围 | 对于 r、g 和 b:0% = 0.0,100% = 255.0 对于 alpha:0% = 0.0,100% = 1.0 |
测试
- rgb-001.html (实时测试) (来源)
- rgb-002.html (实时测试) (来源)
- rgb-003.html (实时测试) (来源)
- rgb-004.html (实时测试) (来源)
- rgb-005.html (实时测试) (来源)
- rgb-006.html (实时测试) (来源)
- rgb-007.html (实时测试) (来源)
- rgb-008.html (实时测试) (来源)
- out-of-gamut-legacy-rgb.html (实时测试) (来源)
- color-valid.html (实时测试) (来源)
- color-computed-rgb.html (实时测试) (来源)
- color-invalid-rgb.html (实时测试) (来源)
- color-valid-rgb.html (实时测试) (来源)
前三个参数分别指定颜色的 r、g 和 b(红、绿、蓝)分量。 0% 表示 sRGB 色域中该颜色分量的最小值, 100% 表示最大值。
颜色分量的百分比参考范围源于这样一个历史事实:许多图形引擎在内部将颜色分量存储为单个字节, 该字节可以容纳 0 到 255 之间的整数。 实现应尽可能遵循创作或计算的分量精度。 如果无法做到,则应将分量向 +∞ 舍入。
最后一个参数 <alpha-value> 指定颜色的 Alpha 值。 如果省略,则默认为 100%。
测试
超出这些范围的值并非无效, 而是在解析值时被限制在此处定义的范围内。
由于历史原因,rgb() 和 rgba() 也支持旧版颜色语法。
测试
5.2. RGB 十六进制表示法:#RRGGBB
CSS 十六进制颜色表示法允许通过将分量指定为十六进制数来指定 sRGB 颜色, 这与颜色通常在计算机代码中直接编写的方式类似。 它也比用 rgb() 表示法写出相同的颜色要短。
<hex-color> 的语法是一个 <hash-token> 标记,其值由 3、4、6 或 8 个十六进制数字组成。 换句话说,十六进制颜色写为一个哈希字符“#”, 后跟一些数字 0-9 或字母 a-f (字母的大小写无关紧要 - #00ff00 与 #00FF00 相同)。
给定的十六进制数字的数量决定了如何将十六进制表示法解码为 RGB 颜色:
- 6 位数字
- 第一对数字,解释为十六进制数, 指定颜色的红色分量, 其中 00 表示最小值, ff(十进制的 255)表示最大值。 下一对数字,以相同的方式解释, 指定绿色分量, 最后一对指定蓝色分量。 颜色的 Alpha 分量是完全不透明的。
- 8 位数字
- 前 6 位数字的解释与 6 位数字表示法相同。 最后一对数字,解释为十六进制数, 指定颜色的 Alpha 分量, 其中 00 表示完全透明的颜色, ff 表示完全不透明的颜色。
- 3 位数字
- 这是 6 位数字表示法的一种较短的变体。 第一个数字,解释为十六进制数, 指定颜色的红色分量, 其中 0 表示最小值, f 表示最大值。 接下来的两个数字分别以相同的方式表示绿色和蓝色分量。 颜色的 Alpha 分量是完全不透明的。
- 4 位数字
- 这是 8 位数字表示法的一种较短的变体, 其“扩展”方式与 3 位数字表示法相同。 第一个数字,解释为十六进制数, 指定颜色的红色分量, 其中 0 表示最小值, f 表示最大值。 接下来的三个数字分别表示绿色、蓝色和 Alpha 分量。
测试
- hex-001.html (实时测试) (来源)
- hex-002.html (实时测试) (来源)
- hex-003.html (实时测试) (来源)
- hex-004.html (实时测试) (来源)
- border-bottom-color.xht (实时测试) (来源)
- border-left-color.xht (实时测试) (来源)
- border-right-color.xht (实时测试) (来源)
- border-top-color.xht (实时测试) (来源)
- color-valid.html (实时测试) (来源)
- color-computed-hex-color.html (实时测试) (来源)
- color-invalid-hex-color.html (实时测试) (来源)
6. 颜色关键词
除了 <color> 的各种数字语法外, CSS 还定义了几组颜色关键字,可以替代使用——每组都有其自身的优点或用例。
6.1. 命名颜色
CSS 定义了一组 命名颜色,这样常见的颜色可以更容易地书写和阅读。 一个 <named-color> 以 <ident> 的形式编写,任何需要 <color> 的地方都接受这种形式。 对于 CSS 定义的 <ident>,所有这些关键词都是 ASCII 大小写不敏感的。
这些名字解析为 sRGB 颜色。
CSS 的 16 个命名颜色最初来自 VGA 调色板,后来被 HTML 采用:aqua、black、blue、fuchsia、gray、green、lime、maroon、navy、olive、purple、red、silver、teal、white 和 yellow。 其余大部分颜色来自 X11 颜色系统的一个版本,该系统用于 Unix 衍生系统的控制台颜色指定,后来被 SVG 采用。
注意:这些颜色名称在这里被标准化,不是因为它们好,而是因为它们的使用和实现已经存在了几十年,标准需要反映现实。 实际上,很难想象每个名字对应的颜色是什么(因此有下面的列表);这些名字在 sRGB 色彩空间中的分布并不均匀,甚至它们内部也不一致( darkgray 比 gray 更亮,而 lightpink 却比 pink 更暗), 并且一些名字(例如 indianred,最初是以来自印度的红色颜料命名的)被发现具有冒犯性。因此,不建议使用它们。
(两个特殊的颜色值 transparent 和 currentcolor 在各自的章节中有特殊定义。)
下表定义了所有不透明的命名颜色,通过给出其他颜色语法的等价数值表示。
| 命名 | 数值 | 颜色名称 | 十六进制 RGB | 十进制 |
|---|---|---|---|---|
| aliceblue | #f0f8ff | 240 248 255 | ||
| antiquewhite | #faebd7 | 250 235 215 | ||
| aqua | #00ffff | 0 255 255 | ||
| aquamarine | #7fffd4 | 127 255 212 | ||
| azure | #f0ffff | 240 255 255 | ||
| beige | #f5f5dc | 245 245 220 | ||
| bisque | #ffe4c4 | 255 228 196 | ||
| black | #000000 | 0 0 0 | ||
| blanchedalmond | #ffebcd | 255 235 205 | ||
| blue | #0000ff | 0 0 255 | ||
| blueviolet | #8a2be2 | 138 43 226 | ||
| brown | #a52a2a | 165 42 42 | ||
| burlywood | #deb887 | 222 184 135 | ||
| cadetblue | #5f9ea0 | 95 158 160 | ||
| chartreuse | #7fff00 | 127 255 0 | ||
| chocolate | #d2691e | 210 105 30 | ||
| coral | #ff7f50 | 255 127 80 | ||
| cornflowerblue | #6495ed | 100 149 237 | ||
| cornsilk | #fff8dc | 255 248 220 | ||
| crimson | #dc143c | 220 20 60 | ||
| cyan | #00ffff | 0 255 255 | ||
| darkblue | #00008b | 0 0 139 | ||
| darkcyan | #008b8b | 0 139 139 | ||
| darkgoldenrod | #b8860b | 184 134 11 | ||
| darkgray | #a9a9a9 | 169 169 169 | ||
| darkgreen | #006400 | 0 100 0 | ||
| darkgrey | #a9a9a9 | 169 169 169 | ||
| darkkhaki | #bdb76b | 189 183 107 | ||
| darkmagenta | #8b008b | 139 0 139 | ||
| darkolivegreen | #556b2f | 85 107 47 | ||
| darkorange | #ff8c00 | 255 140 0 | ||
| darkorchid | #9932cc | 153 50 204 | ||
| darkred | #8b0000 | 139 0 0 | ||
| darksalmon | #e9967a | 233 150 122 | ||
| darkseagreen | #8fbc8f | 143 188 143 | ||
| darkslateblue | #483d8b | 72 61 139 | ||
| darkslategray | #2f4f4f | 47 79 79 | ||
| darkslategrey | #2f4f4f | 47 79 79 | ||
| darkturquoise | #00ced1 | 0 206 209 | ||
| darkviolet | #9400d3 | 148 0 211 | ||
| deeppink | #ff1493 | 255 20 147 | ||
| deepskyblue | #00bfff | 0 191 255 | ||
| dimgray | #696969 | 105 105 105 | ||
| dimgrey | #696969 | 105 105 105 | ||
| dodgerblue | #1e90ff | 30 144 255 | ||
| firebrick | #b22222 | 178 34 34 | ||
| floralwhite | #fffaf0 | 255 250 240 | ||
| forestgreen | #228b22 | 34 139 34 | ||
| fuchsia | #ff00ff | 255 0 255 | ||
| gainsboro | #dcdcdc | 220 220 220 | ||
| ghostwhite | #f8f8ff | 248 248 255 | ||
| gold | #ffd700 | 255 215 0 | ||
| goldenrod | #daa520 | 218 165 32 | ||
| gray | #808080 | 128 128 128 | ||
| green | #008000 | 0 128 0 | ||
| greenyellow | #adff2f | 173 255 47 | ||
| grey | #808080 | 128 128 128 | ||
| honeydew | #f0fff0 | 240 255 240 | ||
| hotpink | #ff69b4 | 255 105 180 | ||
| indianred | #cd5c5c | 205 92 92 | ||
| indigo | #4b0082 | 75 0 130 | ||
| ivory | #fffff0 | 255 255 240 | ||
| khaki | #f0e68c | 240 230 140 | ||
| lavender | #e6e6fa | 230 230 250 | ||
| lavenderblush | #fff0f5 | 255 240 245 | ||
| lawngreen | #7cfc00 | 124 252 0 | ||
| lemonchiffon | #fffacd | 255 250 205 | ||
| lightblue | #add8e6 | 173 216 230 | ||
| lightcoral | #f08080 | 240 128 128 | ||
| lightcyan | #e0ffff | 224 255 255 | ||
| lightgoldenrodyellow | #fafad2 | 250 250 210 | ||
| lightgray | #d3d3d3 | 211 211 211 | ||
| lightgreen | #90ee90 | 144 238 144 | ||
| lightgrey | #d3d3d3 | 211 211 211 | ||
| lightpink | #ffb6c1 | 255 182 193 | ||
| lightsalmon | #ffa07a | 255 160 122 | ||
| lightseagreen | #20b2aa | 32 178 170 | ||
| lightskyblue | #87cefa | 135 206 250 | ||
| lightslategray | #778899 | 119 136 153 | ||
| lightslategrey | #778899 | 119 136 153 | ||
| lightsteelblue | #b0c4de | 176 196 222 | ||
| lightyellow | #ffffe0 | 255 255 224 | ||
| lime | #00ff00 | 0 255 0 | ||
| limegreen | #32cd32 | 50 205 50 | ||
| linen | #faf0e6 | 250 240 230 | ||
| magenta | #ff00ff | 255 0 255 | ||
| maroon | #800000 | 128 0 0 | ||
| mediumaquamarine | #66cdaa | 102 205 170 | ||
| mediumblue | #0000cd | 0 0 205 | ||
| mediumorchid | #ba55d3 | 186 85 211 | ||
| mediumpurple | #9370db | 147 112 219 | ||
| mediumseagreen | #3cb371 | 60 179 113 | ||
| mediumslateblue | #7b68ee | 123 104 238 | ||
| mediumspringgreen | #00fa9a | 0 250 154 | ||
| mediumturquoise | #48d1cc | 72 209 204 | ||
| mediumvioletred | #c71585 | 199 21 133 | ||
| midnightblue | #191970 | 25 25 112 | ||
| mintcream | #f5fffa | 245 255 250 | ||
| mistyrose | #ffe4e1 | 255 228 225 | ||
| moccasin | #ffe4b5 | 255 228 181 | ||
| navajowhite | #ffdead | 255 222 173 | ||
| navy | #000080 | 0 0 128 | ||
| oldlace | #fdf5e6 | 253 245 230 | ||
| olive | #808000 | 128 128 0 | ||
| olivedrab | #6b8e23 | 107 142 35 | ||
| orange | #ffa500 | 255 165 0 | ||
| orangered | #ff4500 | 255 69 0 | ||
| orchid | #da70d6 | 218 112 214 | ||
| palegoldenrod | #eee8aa | 238 232 170 | ||
| palegreen | #98fb98 | 152 251 152 | ||
| paleturquoise | #afeeee | 175 238 238 | ||
| palevioletred | #db7093 | 219 112 147 | ||
| papayawhip | #ffefd5 | 255 239 213 | ||
| peachpuff | #ffdab9 | 255 218 185 | ||
| peru | #cd853f | 205 133 63 | ||
| pink | #ffc0cb | 255 192 203 | ||
| plum | #dda0dd | 221 160 221 | ||
| powderblue | #b0e0e6 | 176 224 230 | ||
| purple | #800080 | 128 0 128 | ||
| rebeccapurple | #663399 | 102 51 153 | ||
| red | #ff0000 | 255 0 0 | ||
| rosybrown | #bc8f8f | 188 143 143 | ||
| royalblue | #4169e1 | 65 105 225 | ||
| saddlebrown | #8b4513 | 139 69 19 | ||
| salmon | #fa8072 | 250 128 114 | ||
| sandybrown | #f4a460 | 244 164 96 | ||
| seagreen | #2e8b57 | 46 139 87 | ||
| seashell | #fff5ee | 255 245 238 | ||
| sienna | #a0522d | 160 82 45 | ||
| silver | #c0c0c0 | 192 192 192 | ||
| skyblue | #87ceeb | 135 206 235 | ||
| slateblue | #6a5acd | 106 90 205 | ||
| slategray | #708090 | 112 128 144 | ||
| slategrey | #708090 | 112 128 144 | ||
| snow | #fffafa | 255 250 250 | ||
| springgreen | #00ff7f | 0 255 127 | ||
| steelblue | #4682b4 | 70 130 180 | ||
| tan | #d2b48c | 210 180 140 | ||
| teal | #008080 | 0 128 128 | ||
| thistle | #d8bfd8 | 216 191 216 | ||
| tomato | #ff6347 | 255 99 71 | ||
| turquoise | #40e0d0 | 64 224 208 | ||
| violet | #ee82ee | 238 130 238 | ||
| wheat | #f5deb3 | 245 222 179 | ||
| white | #ffffff | 255 255 255 | ||
| whitesmoke | #f5f5f5 | 245 245 245 | ||
| yellow | #ffff00 | 255 255 0 | ||
| yellowgreen | #9acd32 | 154 205 50 |
注意:此颜色列表及其定义是 SVG 1.1 定义的命名颜色 列表的超集。
为了历史原因,这也被称为 X11 颜色集。注意: X11 颜色系统的历史非常有趣,并由 Alex Sexton 在其演讲 “Peachpuffs and Lemonchiffons” 中做了极好的总结。
测试
6.2. 系统颜色
一般来说,<system-color> 关键字 反映了用户、浏览器或操作系统所做的默认颜色选择。 它们通常用于浏览器的默认样式表中,正是出于这个原因。
为了保持可读性, <system-color> 关键字也会响应 元素配色方案。
可读的链接文本
不可读的链接文本
可读的链接文本
然而,在 强制配色模式(forced colors mode)下, 页面上的大多数颜色会被强制为受用户选择限制的调色板, 详见 CSS Color Adjustment 1 § 5.2 强制配色模式调色板。 <system-color> 关键字 将这些用户选择的颜色暴露出来, 方便页面其他部分能够融合进这个受限调色板。
当 forced-colors 媒体特性 处于 active 状态时, 作者应当在<system-color> 关键字用作颜色值, 用在CSS Color Adjustment 1 § 3.1 列出的受影响属性以外的属性上, 以确保整个页面的可读性和一致性, 并避免用户强制颜色和页面自定义颜色混杂的情况。
测试
当<system-color>关键字的值由浏览器提供 (而非操作系统默认或用户自定义)时,浏览器应该 保证匹配的 前景/背景配对至少有 WCAG AA 对比度。 但用户偏好(无论是更高或更低的对比度), 无论是通过浏览器偏好、用户样式表, 还是更改操作系统默认值, 都必须优先于该要求。
作者也可以在任何时候使用这些关键字, 但应当注意使用成对匹配的背景-前景色, 以保证合适的对比度。 因为非配对项之间 的对比关系 (例如 Canvas 和 ButtonText) 并无法保证。
<system-color> 关键字定义如下:
- AccentColor
- 强调型界面控件的背景色。
- AccentColorText
- 强调型界面控件的文本色。
- ActiveText
- 活动链接文本色。浅色背景下通常为红色。
- ButtonBorder
- 普通按钮的基础边框色。
- ButtonFace
- 普通按钮的表面(face)背景色。
- ButtonText
- 按钮文本色。
- Canvas
- 应用内容或文档的背景色。
- CanvasText
- 应用内容或文档的文本色。
- Field
- 输入框的背景色。
- FieldText
- 输入框中的文本色。
- GrayText
- 禁用文本色。 (通常但不一定为灰色。)
- Highlight
- 选中文本的背景色,例如 ::selection。
- HighlightText
- 选中文本的文本色。
- LinkText
- 非活动、未访问链接的文本色。浅色背景下传统为蓝色。
- Mark
- 被特殊标记文本的背景色
(例如HTML的
mark元素)。 - MarkText
- 被特殊标记文本的文本色
(例如HTML的
mark元素)。 - SelectedItem
- 被选中项的背景色,例如被选中的复选框。
- SelectedItemText
- 被选中项的文本色。
- VisitedText
- 已访问链接的文本色。浅色背景下传统为紫色。
测试
注意:与所有其他关键字一样,这些名称是ASCII 大小写不敏感的。它们在这里以混合大小写显示以便于阅读。
对于没有特定系统 UI 概念的系统,指定的值应映射到最接近的相关系统颜色值。以下系统颜色配对预计将形成可读的背景-前景颜色:
-
Canvas背景与ButtonBorder边框以及相邻颜色Canvas
-
ButtonFace背景与ButtonText前景配对。
-
ButtonFace或Field背景与ButtonBorder边框以及相邻颜色Canvas
-
Highlight背景与HighlightText前景配对。
-
SelectedItem背景与SelectedItemText前景配对。
-
AccentColor背景与AccentColorText前景配对。
此外,GrayText预计可以阅读,尽管对比度可能较低,在任何背景上都可以阅读。
为保持与组件强调色样式的一致性, AccentColor 的取值来自 accent-color, 除非启用了强制色彩模式。 AccentColorText 的取值为 和 AccentColor 对比的前景色, 具体如组件 强调色样式中所描述。
Canvas 与 CanvasText:CanvasText
Canvas 与 LinkText:LinkText
Canvas 与 VisitedText:VisitedText
Canvas 与 ActiveText:ActiveText
Canvas 与 GrayText:GrayText
Canvas 与 ButtonBorder 以及相邻的 Canvas:CanvasText相邻
ButtonFace 与 ButtonText:ButtonText
ButtonFace 与 ButtonText 和 ButtonBorder:ButtonText
ButtonFace 与 GrayText:GrayText
Field 与 FieldText:FieldText
Field 与 GrayText:GrayText
Mark 与 MarkText:MarkText
Mark 与 GrayText:GrayText
Highlight 与 HighlightText:HighlightText
Highlight 与 GrayText:GrayText
SelectedItem 与 SelectedItemText:SelectedItemText
AccentColor 与 AccentColorText:AccentColorText
AccentColor 与 GrayText:GrayText
早期版本的CSS定义了其他<系统颜色>,这些已经被弃用。它们在附录A:弃用的CSS系统颜色中进行了记录。
注意:<系统颜色>存在一些隐私和安全风险,详见§ 22 隐私考虑和§ 20 安全考虑。
用户代理可以选择返回固定值以减轻隐私和安全风险(例如指纹识别),这些值不反映用户的自定义或主题选择。
6.3. transparent关键字
关键字transparent指定了透明黑。它是一种<named-color>。
测试
6.4. currentcolor关键字
关键字currentcolor表示同一元素上的color属性的值。与<named-color>不同,它不局限于 sRGB;该值可以是任何<color>。其使用值由解析颜色值来确定。
测试
- border-color-currentcolor.html (实时测试) (来源)
- color-mix-currentcolor-nested-for-color-property.html (实时测试) (来源)
- currentcolor-001.html (实时测试) (来源)
- currentcolor-002.html (实时测试) (来源)
- currentcolor-003.html (实时测试) (来源)
- currentcolor-004.html (实时测试) (来源)
- currentcolor-visited-fallback.html (实时测试) (来源)
- color-valid.html (实时测试) (来源)
.foo{ color : red; background-color : currentcolor; }
这相当于写成:
.foo{ color : red; background-color : red; }
< p >< em > Some< strong > really</ strong > emphasized text.</ em > < style > p { color : black ; } em { text-emphasis : dot ; } strong { color : red ; } </ style >

在上面的示例中,强调标记在文本 "Some" 和 "emphasized text" 上为黑色,但在文本 "really" 上为红色。
注意: CSS 中的多词关键字通常用连字符分隔各个组成词。 currentcolor 没有这样做,因为(深呼吸) 它最初在 SVG 中作为属性值引入,名字写作 "current-color",符合 CSS 通常拼法。 后来(以及所有其他属性及其值) 它变成了展示属性和值, 以及属性, 以便用 XSLT 方便生成。 接着所有展示属性都从用连字符的写法 改成了驼峰式,因为 DOM 对连字符含义为“减号”有问题。 但这样又不符合 CSS 的习惯写法 所以所有已经被 CSS 采用的属性及属性值 又都改回用连字符了! currentcolor 那时还没进 CSS, 所以还是 camelCase。 CSS 后来才采用它, 这时候大小写就无所谓了, 因为 CSS 关键字 ASCII 不区分大小写。
7. HSL 颜色:hsl() 和 hsla() 函数
RGB 系统用于指定颜色, 对于机器和图形库来说很方便, 但通常被认为对人类来说很难直观地理解。 例如,很难知道如何改变 RGB 颜色以产生同一色调的较亮变体。
还有几种其他的颜色方案可行。 其中一种是 HSL [HSL] 颜色方案, 使用起来更直观, 但仍可以轻松映射回 RGB 颜色。
HSL 颜色是通过色相、饱和度和亮度三元组来指定的。 hsl() 和 hsla() 函数的语法是:
hsl () =[ <legacy-hsl-syntax> | <modern-hsl-syntax>] hsla () =[ <legacy-hsla-syntax> | <modern-hsla-syntax>] <modern-hsl-syntax> =hsl ( [ <hue> | none] [ <percentage> | <number> | none] [ <percentage> | <number> | none] [ /[ <alpha-value> | none] ] ?) <modern-hsla-syntax> =hsla ( [ <hue> | none] [ <percentage> | <number> | none] [ <percentage> | <number> | none] [ /[ <alpha-value> | none] ] ?) <legacy-hsl-syntax> =hsl ( <hue>, <percentage>, <percentage>, <alpha-value>?) <legacy-hsla-syntax> =hsla ( <hue>, <percentage>, <percentage>, <alpha-value>?)
| 百分比 | 适用于 S 和 L |
|---|---|
| 百分比参考范围 | 对于 S 和 L:0% = 0.0,100% = 100.0 |
| 无效色相 ε | S <= 0.001 |
测试
- hsl-001.html (实时测试) (来源)
- hsl-002.html (实时测试) (来源)
- hsl-003.html (实时测试) (来源)
- hsl-004.html (实时测试) (来源)
- hsl-005.html (实时测试) (来源)
- hsl-006.html (实时测试) (来源)
- hsl-007.html (实时测试) (来源)
- hsl-008.html (实时测试) (来源)
- hsl-clamp-negative-saturation.html (实时测试) (来源)
- background-color-hsl-001.html (实时测试) (来源)
- background-color-hsl-002.html (实时测试) (来源)
- background-color-hsl-003.html (实时测试) (来源)
- background-color-hsl-004.html (实时测试) (来源)
- color-computed-hsl.html (实时测试) (来源)
- color-invalid-hsl.html (实时测试) (来源)
- color-valid-hsl.html (实时测试) (来源)
第一个参数指定色相角度。
在 HSL(和 HWB)中,角度 0deg 代表 sRGB 原始红色(同样适用于 360deg、720deg 等),其余的色相围绕圆周分布,所以 120deg 代表 sRGB 原始绿色,240deg 代表 sRGB 原始蓝色,依此类推。
接下来的两个参数分别是饱和度和亮度。对于饱和度,100% 或 100 表示完全饱和、明亮的颜色,而 0% 或 0 表示完全不饱和的灰色。对于亮度,50% 或 50 表示“正常”颜色,而 100% 或 100 是白色,0% 或 0 是黑色。
由于历史原因,如果饱和度小于 0%,它将在解析值阶段被钳制到 0%,然后再转换为 sRGB 颜色。
最后一个参数指定颜色的 Alpha 分量。 它的解释与 rgb() 函数的第四个参数完全相同。 如果省略,则默认为 100%。
HSL 颜色解析为 sRGB。
如果 HSL 颜色的饱和度为 0% 或 0,则色相分量是无效的。
与 RGB 相比,HSL 的一个优点是它更直观:人们可以猜测他们想要的颜色,然后进行调整。
hsl ( 120 deg 100 % 50 % ) 亮绿色hsl ( 120 deg 100 % 25 % ) 深绿色hsl ( 120 deg 100 % 75 % ) 浅绿色hsl ( 120 deg 75 % 85 % ) 粉绿色
HSL 相对于 OKLCh 的一个缺点是,色相操作会改变视觉亮度,并且色相并非均匀分布。
因此,在 HSL 中创建一组匹配的颜色(通过保持色相不变并改变饱和度和亮度)比操作 sRGB 组件值更容易;但是,由于亮度只是伽马校正的红、绿和蓝组件的平均值,因此它不对应于色相的视觉亮度。
在 OKLCh 中,sRGB 蓝色为 oklch(0.452 0.313 264.1),而 sRGB 黄色为 oklch(0.968 0.211 109.8)。OKLCh 的亮度值 0.452 和 0.968 明确地反映了两种颜色的视觉亮度。
在 HSL 中,色相角度并非感知上均匀的;颜色在某些区域聚集,而在其他区域则较为稀疏。
在 OKLCh 中,相同的颜色对 oklch(0.533 0.26 262.6) 和 oklch(0.462 0.306 268.9) 的色相差异为 268.9 - 262.6 = 6.3度,而另一对 oklch(0.882 0.181 94.24) 和 oklch(0.91 0.245 129.9) 的色相差异为 129.9 - 94.24 = 35.66度,正确地反映了色相的视觉分离程度。
由于历史原因,hsl() 和 hsla() 也支持旧版颜色语法。
测试
7.1. 将 HSL 颜色转换为 sRGB
将 HSL 颜色转换为 sRGB 在数学上很简单。 以下是 JavaScript 中转换算法的示例实现。 它返回一个包含三个数字的数组, 分别表示颜色的红色、绿色和蓝色分量, 对于 sRGB 色域中的颜色,这些分量将在 [0, 1] 范围内。
此代码假设已经应用了解析时对负饱和度的限制。
/** * @param {number} hue - Hue as degrees 0..360 * @param {number} sat - Saturation in reference range [0,100] * @param {number} light - Lightness in reference range [0,100] * @return {number[]} Array of sRGB components; in-gamut colors in range [0..1] */ function hslToRgb( hue, sat, light) { sat/= 100 ; light/= 100 ; function f( n) { let k= ( n+ hue/ 30 ) % 12 ; let a= sat* Math. min( light, 1 - light); return light- a* Math. max( - 1 , Math. min( k- 3 , 9 - k, 1 )); } return [ f( 0 ), f( 8 ), f( 4 )]; }
7.2. 将 sRGB 颜色转换为 HSL
反向的转换过程类似。
特别注意处理由远离 sRGB 色域的颜色产生的中间负饱和度值。
/** * @param {number} red - Red component 0..1 * @param {number} green - Green component 0..1 * @param {number} blue - Blue component 0..1 * @return {number[]} Array of HSL values: Hue as degrees 0..360, Saturation and Lightness in reference range [0,100] */ function rgbToHsl( red, green, blue) { let max= Math. max( red, green, blue); let min= Math. min( red, green, blue); let [ hue, sat, light] = [ NaN , 0 , ( min+ max) / 2 ]; let d= max- min; let epsilon= 1 / 100000 ; // max Sat is 1, in this code if ( d!== 0 ) { sat= ( light=== 0 || light=== 1 ) ? 0 : ( max- light) / Math. min( light, 1 - light); switch ( max) { case red: hue= ( green- blue) / d+ ( green< blue? 6 : 0 ); break ; case green: hue= ( blue- red) / d+ 2 ; break ; case blue: hue= ( red- green) / d+ 4 ; } hue= hue* 60 ; } // Very out of gamut colors can produce negative saturation // If so, just rotate the hue by 180 and use a positive saturation // see https://github.com/w3c/csswg-drafts/issues/9222 if ( sat< 0 ) { hue+= 180 ; sat= Math. abs( sat); } if ( hue>= 360 ) { hue-= 360 ; } if ( sat<= epsilon) { hue= NaN ; } return [ hue, sat* 100 , light* 100 ]; }
7.3. HSL 颜色示例
本节不是规范性的。
测试
本节不是规范性的,因此不需要测试。
下表展示了可能的 HSL 颜色的广泛范围。 每个表格代表一个色相, 以 30° 为间隔选择, 以说明常见的 "核心" 色调: 红色, 黄色, 绿色, 青色, 蓝色, 洋红色, 以及这些之间的六种中间颜色。
在每个表格中,X 轴表示饱和度, Y 轴表示亮度。
| 0° 红色 | ||||||
|---|---|---|---|---|---|---|
| 100% | 80% | 60% | 40% | 20% | 0% | |
| 100% | ||||||
| 90% | ||||||
| 80% | ||||||
| 70% | ||||||
| 60% | ||||||
| 50% | ||||||
| 40% | ||||||
| 30% | ||||||
| 20% | ||||||
| 10% | ||||||
| 0% | ||||||
| 30° 红-黄(=橙色) | ||||||
|---|---|---|---|---|---|---|
| 100% | 80% | 60% | 40% | 20% | 0% | |
| 100% | ||||||
| 90% | ||||||
| 80% | ||||||
| 70% | ||||||
| 60% | ||||||
| 50% | ||||||
| 40% | ||||||
| 30% | ||||||
| 20% | ||||||
| 10% | ||||||
| 0% | ||||||
| 60° 黄色 | ||||||
|---|---|---|---|---|---|---|
| 100% | 80% | 60% | 40% | 20% | 0% | |
| 100% | ||||||
| 90% | ||||||
| 80% | ||||||
| 70% | ||||||
| 60% | ||||||
| 50% | ||||||
| 40% | ||||||
| 30% | ||||||
| 20% | ||||||
| 10% | ||||||
| 0% | ||||||
| 90° 黄绿色 | ||||||
|---|---|---|---|---|---|---|
| 100% | 80% | 60% | 40% | 20% | 0% | |
| 100% | ||||||
| 90% | ||||||
| 80% | ||||||
| 70% | ||||||
| 60% | ||||||
| 50% | ||||||
| 40% | ||||||
| 30% | ||||||
| 20% | ||||||
| 10% | ||||||
| 0% | ||||||
| 120° 绿色 | ||||||
|---|---|---|---|---|---|---|
| 100% | 80% | 60% | 40% | 20% | 0% | |
| 100% | ||||||
| 90% | ||||||
| 80% | ||||||
| 70% | ||||||
| 60% | ||||||
| 50% | ||||||
| 40% | ||||||
| 30% | ||||||
| 20% | ||||||
| 10% | ||||||
| 0% | ||||||
| 150° 绿青色 | ||||||
|---|---|---|---|---|---|---|
| 100% | 80% | 60% | 40% | 20% | 0% | |
| 100% | ||||||
| 90% | ||||||
| 80% | ||||||
| 70% | ||||||
| 60% | ||||||
| 50% | ||||||
| 40% | ||||||
| 30% | ||||||
| 20% | ||||||
| 10% | ||||||
| 0% | ||||||
| 180° 青色 | ||||||
|---|---|---|---|---|---|---|
| 100% | 80% | 60% | 40% | 20% | 0% | |
| 100% | ||||||
| 90% | ||||||
| 80% | ||||||
| 70% | ||||||
| 60% | ||||||
| 50% | ||||||
| 40% | ||||||
| 30% | ||||||
| 20% | ||||||
| 10% | ||||||
| 0% | ||||||
| 210° 青-蓝色 | ||||||
|---|---|---|---|---|---|---|
| 100% | 80% | 60% | 40% | 20% | 0% | |
| 100% | ||||||
| 90% | ||||||
| 80% | ||||||
| 70% | ||||||
| 60% | ||||||
| 50% | ||||||
| 40% | ||||||
| 30% | ||||||
| 20% | ||||||
| 10% | ||||||
| 0% | ||||||
| 240° 蓝色 | ||||||
|---|---|---|---|---|---|---|
| 100% | 80% | 60% | 40% | 20% | 0% | |
| 100% | ||||||
| 90% | ||||||
| 80% | ||||||
| 70% | ||||||
| 60% | ||||||
| 50% | ||||||
| 40% | ||||||
| 30% | ||||||
| 20% | ||||||
| 10% | ||||||
| 0% | ||||||
| 270° 蓝-洋红 | ||||||
|---|---|---|---|---|---|---|
| 100% | 80% | 60% | 40% | 20% | 0% | |
| 100% | ||||||
| 90% | ||||||
| 80% | ||||||
| 70% | ||||||
| 60% | ||||||
| 50% | ||||||
| 40% | ||||||
| 30% | ||||||
| 20% | ||||||
| 10% | ||||||
| 0% | ||||||
| 300° 洋红 | ||||||
|---|---|---|---|---|---|---|
| 100% | 80% | 60% | 40% | 20% | 0% | |
| 100% | ||||||
| 90% | ||||||
| 80% | ||||||
| 70% | ||||||
| 60% | ||||||
| 50% | ||||||
| 40% | ||||||
| 30% | ||||||
| 20% | ||||||
| 10% | ||||||
| 0% | ||||||
| 330° 洋红-红 | ||||||
|---|---|---|---|---|---|---|
| 100% | 80% | 60% | 40% | 20% | 0% | |
| 100% | ||||||
| 90% | ||||||
| 80% | ||||||
| 70% | ||||||
| 60% | ||||||
| 50% | ||||||
| 40% | ||||||
| 30% | ||||||
| 20% | ||||||
| 10% | ||||||
| 0% | ||||||
8. HWB 颜色:hwb() 函数
HWB(色相-白度-黑度的缩写)[HWB] 是另一种指定 sRGB 颜色的方法,类似于HSL,但通常对人类来说更容易使用。它通过一个起始色相,然后混入一定程度的白色和黑色来描述颜色。
许多颜色选择器基于 HWB 色彩系统,因为它直观易用。
HWB 颜色解析为 sRGB。
< input type = "color" >
时显示。外部的轮盘用于选择色相,然后通过单击内部三角形选择相对的白色和黑色量。hwb() 函数的语法如下:
hwb () =hwb ( [ <hue> | none] [ <percentage> | <number> | none] [ <percentage> | <number> | none] [ /[ <alpha-value> | none] ] ?)
| 百分比 | 允许用于 W 和 B |
|---|---|
| 百分比参考范围 | 对于 W 和 B:0% = 0.0,100% = 100.0 |
| 无效色相 ε | W + B >= 99.999 |
第一个参数指定色相, 其定义与 hsl() 完全相同; 这意味着它具有相同的缺点,例如色相均匀性。
第二个参数指定要混合的白色量, 百分比范围从 0%(无白色)到 100%(全白色)。 类似地,第三个参数指定要混合的黑色量, 百分比范围也从 0%(无黑色)到 100%(全黑色)。
注: hwb() 未考虑阿布尼效应 [Abney] ,该效应是指向单色光源中加入白光时 所产生的感知色相偏移。
超出这些范围的值并不无效;色相角度超出 [0,360) 范围将被归一化至该范围,而白色和黑色的值相加为 100% 或更高时,将生成如下所述的无彩色。
最终的颜色可以在概念上理解为选择的色相、白色颜料和黑色颜料的混合物,相对的数量由百分比决定。
如果白色加黑色的和大于或等于 100%,则定义为无彩色,即灰色阴影;转换为 sRGB 时,R、G 和 B 的值相等,其值为 white / (white + black)。
无彩色的 HWB 颜色不再包含任何所选择的色相。在这种情况下,色相组件为无效。
第四个参数指定颜色的 Alpha 分量。 它的解释与 rgb() 函数的第四个参数完全相同。 如果省略,则默认为 100%。
hwb 不存在 Web 兼容性问题,它 是本级规范中新引入的,因此 hwb() 不支持使用逗号分隔其所有参数的旧式颜色语法。 在 hwb() 中使用逗号是错误的。
测试
8.1. 将 HWB 颜色转换为 sRGB
将 HWB 颜色转换为 sRGB 非常简单,类似于将 HSL 转换为 RGB 的方式。以下是算法的 Javascript 实现,首先将白色和黑色组件标准化,使它们的总和不超过 100%。
/** * @param {number} hue - Hue as degrees 0..360 * @param {number} white - Whiteness in reference range [0,100] * @param {number} black - Blackness in reference range [0,100] * @return {number[]} Array of RGB components 0..1 */ function hwbToRgb( hue, white, black) { white/= 100 ; black/= 100 ; if ( white+ black>= 1 ) { let gray= white/ ( white+ black); return [ gray, gray, gray]; } let rgb= hslToRgb( hue, 100 , 50 ); for ( let i= 0 ; i< 3 ; i++ ) { rgb[ i] *= ( 1 - white- black); rgb[ i] += white; } return rgb; }
8.2. 将 sRGB 颜色转换为 HWB
反向转换的过程类似。
/** * @param {number} red - Red component 0..1 * @param {number} green - Green component 0..1 * @param {number} blue - Blue component 0..1 * @return {number} Hue as degrees 0..360 */ function rgbToHue( red, green, blue) { // Similar to rgbToHsl, except that saturation and lightness are not calculated, and // potential negative saturation is ignored. let max= Math. max( red, green, blue); let min= Math. min( red, green, blue); let hue= NaN ; let d= max- min; if ( d!== 0 ) { switch ( max) { case red: hue= ( green- blue) / d+ ( green< blue? 6 : 0 ); break ; case green: hue= ( blue- red) / d+ 2 ; break ; case blue: hue= ( red- green) / d+ 4 ; } hue*= 60 ; } if ( hue>= 360 ) { hue-= 360 ; } return hue; } /** * @param {number} red - Red component 0..1 * @param {number} green - Green component 0..1 * @param {number} blue - Blue component 0..1 * @return {number[]} Array of HWB values: Hue as degrees 0..360, Whiteness and Blackness in reference range [0,100] */ function rgbToHwb( red, green, blue) { let epsilon= 1 / 100000 ; // account for multiply by 100 var hue= rgbToHue( red, green, blue); var white= Math. min( red, green, blue); var black= 1 - Math. max( red, green, blue); if ( white+ black>= 1 - epsilon) { hue= NaN ; } return ([ hue, white* 100 , black* 100 ]); }
8.3. HWB 颜色示例
本节不是规范性内容。
测试
本节不是规范性内容,不需要测试。
| 0° 红色 | ||||||
|---|---|---|---|---|---|---|
| W\B | 0% | 20% | 40% | 60% | 80% | 100% |
| 0% | ||||||
| 20% | ||||||
| 40% | ||||||
| 60% | ||||||
| 80% | ||||||
| 100% | ||||||
| 30° 红-黄色(橙色) | ||||||
|---|---|---|---|---|---|---|
| W\B | 0% | 20% | 40% | 60% | 80% | 100% |
| 0% | ||||||
| 20% | ||||||
| 40% | ||||||
| 60% | ||||||
| 80% | ||||||
| 100% | ||||||
| 60° 黄色 | ||||||
|---|---|---|---|---|---|---|
| W\B | 0% | 20% | 40% | 60% | 80% | 100% |
| 0% | ||||||
| 20% | ||||||
| 40% | ||||||
| 60% | ||||||
| 80% | ||||||
| 100% | ||||||
| 90° 黄-绿色 | ||||||
|---|---|---|---|---|---|---|
| W\B | 0% | 20% | 40% | 60% | 80% | 100% |
| 0% | ||||||
| 20% | ||||||
| 40% | ||||||
| 60% | ||||||
| 80% | ||||||
| 100% | ||||||
| 120° 绿色 | ||||||
|---|---|---|---|---|---|---|
| W\B | 0% | 20% | 40% | 60% | 80% | 100% |
| 0% | ||||||
| 20% | ||||||
| 40% | ||||||
| 60% | ||||||
| 80% | ||||||
| 100% | ||||||
| 150° 绿-青色 | ||||||
|---|---|---|---|---|---|---|
| W\B | 0% | 20% | 40% | 60% | 80% | 100% |
| 0% | ||||||
| 20% | ||||||
| 40% | ||||||
| 60% | ||||||
| 80% | ||||||
| 100% | ||||||
| 180° 青色 | ||||||
|---|---|---|---|---|---|---|
| W\B | 0% | 20% | 40% | 60% | 80% | 100% |
| 0% | ||||||
| 20% | ||||||
| 40% | ||||||
| 60% | ||||||
| 80% | ||||||
| 100% | ||||||
| 210° 青-蓝色 | ||||||
|---|---|---|---|---|---|---|
| W\B | 0% | 20% | 40% | 60% | 80% | 100% |
| 0% | ||||||
| 20% | ||||||
| 40% | ||||||
| 60% | ||||||
| 80% | ||||||
| 100% | ||||||
| 240° 蓝色 | ||||||
|---|---|---|---|---|---|---|
| W\B | 0% | 20% | 40% | 60% | 80% | 100% |
| 0% | ||||||
| 20% | ||||||
| 40% | ||||||
| 60% | ||||||
| 80% | ||||||
| 100% | ||||||
| 270° 蓝-品红色 | ||||||
|---|---|---|---|---|---|---|
| W\B | 0% | 20% | 40% | 60% | 80% | 100% |
| 0% | ||||||
| 20% | ||||||
| 40% | ||||||
| 60% | ||||||
| 80% | ||||||
| 100% | ||||||
| 300° 品红色 | ||||||
|---|---|---|---|---|---|---|
| W\B | 0% | 20% | 40% | 60% | 80% | 100% |
| 0% | ||||||
| 20% | ||||||
| 40% | ||||||
| 60% | ||||||
| 80% | ||||||
| 100% | ||||||
| 330° 品红-红色 | ||||||
|---|---|---|---|---|---|---|
| W\B | 0% | 20% | 40% | 60% | 80% | 100% |
| 0% | ||||||
| 20% | ||||||
| 40% | ||||||
| 60% | ||||||
| 80% | ||||||
| 100% | ||||||
9. 设备无关颜色:CIE Lab 和 LCH,Oklab 和 OKLCh
9.1. CIE Lab 和 LCH
本节不具规范性。
测试
本节不具规范性,不需要测试。
颜色的物理测量通常表示为 CIE L*a*b* [CIELAB] 色彩空间, 该空间由 CIE 于 1976 年创建,通常简称为 Lab。 从一个设备转换到另一个设备的颜色也可以使用 Lab 作为中间步骤。 Lab 是基于人类视觉实验得出的, 代表了人类可见的所有颜色范围。
Lab 是一个具有中央亮度(L)轴的直角坐标系统。 该值通常表示为无单位的数字; 为了与 CSS 其他部分兼容,也可以写为百分比。 100% 表示 L 值为 100,而不是 1.0。 L=0% 或 0 是深黑色(完全没有光) 而 L=100% 或 100 是漫反射白。
有用的是,L=50% 或 50 是中灰色,且设计上, L 的等增量在视觉上是均匀间隔的: Lab 色彩空间旨在实现感知上的均匀性。
a 和 b 轴传递色相; a 轴正值表示紫红色, 而负值则是互补色,绿色。 类似地,b 轴正值表示黄色, 负值表示互补色蓝色/紫色。 去饱和的颜色具有较小的 a 和 b 值, 并靠近 L 轴; 饱和的颜色则远离 L 轴。
光源是 D50 白光,这是一种标准化的日光光谱,色温为 5000K, 由完美漫反射体反射;它近似于晴天阳光的颜色。 D50 也是 ICC 颜色相互转换中配置文件连接空间使用的白点, 是提供 Lab 编辑功能的图像编辑器中使用的白点, 也是物理测量设备(如分光光度计和分光辐射计) 在报告 Lab 测量颜色时使用的值。
使用其他白点指定颜色的转换被称为 色度适应变换, 其模拟了人类视觉系统在适应新的照明条件时的变化。 线性布拉德福德算法 [ICC](原布拉德福德算法的简化版 [Bradford-CAT]) 是行业标准的色度适应变换, 计算非常简单,因为它只是一个矩阵乘法。
CIE LCH 与 Lab 具有相同的 L 轴, 但使用极坐标 C(色度)和 H(色相), 形成了一个极坐标的柱面坐标系统。 C 是从 L 轴的几何距离, H 是从 a 轴正向到 b 轴正向的角度。
注:Lab 和 LCH 的 L 轴 不要与 HSL 的 L 轴混淆。 例如,在 HSL 中,sRGB 颜色蓝色(#00F)和黄色(#FF0) 具有相同的 L 值(50%),即使在视觉上,蓝色要暗得多。 在 Lab 中这就非常清晰: sRGB 蓝色为 lab(29.567% 68.298 -112.0294) 而 sRGB 黄色为 lab(97.607% -15.753 93.388)。 在 Lab 和 LCH 中,如果两个颜色具有相同的 L 值, 那么它们在视觉亮度上是相同的。 HSL 和相关的极坐标 RGB 模型是为了试图 提供类似于 LCH 给 Lab 的可用性好处的 RGB, 但其准确性显著低于 Lab 和 LCH。
尽管 CIE Lab 和 LCH 得到了广泛使用, 但它们也存在一些问题,尤其是:
- 色相线性度
- 在蓝色区域(LCH 色相介于 270° 和 330° 之间), 视觉色相偏离了 LCH 的预测。 如果绘制一组相同色相且色度不同的蓝色, 这些颜色应该形成一条从中性轴延伸的直线, 但实际上形成了曲线。 换句话说, 随着一个饱和的蓝色的色度逐渐减少, 它会变得显著偏紫。
- 色相均匀性
- 虽然 LCH 中的色相通常是均匀间隔的, (且远优于 HSL 或 HWB), 但均匀性并不完美。
- 高色度差异的过度预测
- 对于高色度的颜色, 色度的变化不如中性色更明显。
这些缺陷影响了,例如, 创建均匀间隔的渐变, 将一个色彩空间映射到一个较小的色彩空间, 以及计算两个颜色之间的视觉差异。
为弥补这一点, 预测两个颜色之间视觉差异的公式 (Delta E) 随时间的推移变得更加准确 (但也更复杂)。 当前的行业标准公式, Delta E 2000, 对于缓解 Lab 和 LCH 的某些问题效果良好。 § 20.2 ΔE2000 中给出了示例实现。
但是这对色相曲率问题没有帮助。
9.2. Oklab 和 OKLCh
本节不具规范性。
测试
本节不具规范性,不需要测试。
最近,Oklab,一种改进的类 Lab 空间被开发出来 [Oklab]。 对应的极坐标形式称为 OKLCh。 它是通过对大量视觉上相似的颜色数据集进行数值优化而产生的, 与 CIE LCH 相比,其色相线性度、色相均匀性和色度均匀性都有所提高。
与 CIE Lab 一样,Oklab 有一个中心的亮度 L 轴, 通常表示为范围 [0,1] 的无单位数值; 为了与 CSS 其他部分兼容, 也可以写为百分比。100% 表示 L 值为 1.0。 L=0% 或 0.0 表示深黑色(完全没有光),而 L=100% 或 1.0 表示漫反射白。
注: 与 CIE Lab 不同,CIE Lab 假设适应漫反射白色, 而 Oklab 假设适应被定义的颜色, 这旨在使其具有比例不变性。
与 CIE Lab 一样,a 和 b 轴传递色相; a 轴正值表示紫红色, 负值表示互补色,绿色。 类似地,b 轴正值表示黄色, 负值表示互补的蓝色/紫色。
光源是 D65,与大多数 RGB 色彩空间使用的白点相同。
OKLCh 与 Oklab 具有相同的 L 轴, 但使用极坐标 C(色度)和 H(色相)。
注意:与 CIE LCH 中色度可以达到 200 或更高不同, OKLCh 色度范围约为 0.5。 CIE LCH 和 OKLCh 之间的色相角大致相似, 但不完全相同。
由于 Oklab 的感知均匀性优于 CIE Lab, 因此颜色差异可以通过 3D 空间中的距离(平方和的平方根)直接计算。 尽管计算简单, § 20.3 ΔEOK 中给出了示例实现。
9.3. 指定 Lab 和 LCH:lab() 和 lch() 函数表示法
CSS 允许直接以 Lab 和 LCH 表示颜色。
lab () =lab ( [ <percentage> | <number> | none] [ <percentage> | <number> | none] [ <percentage> | <number> | none] [ /[ <alpha-value> | none] ] ?)
| 百分比 | 允许用于 L, a 和 b |
|---|---|
| 百分比参考范围 | 对于 L:0% = 0.0, 100% = 100.0 对于 a 和 b:-100% = -125, 100% = 125 |
测试
- lab-001.html (实时测试) (来源)
- lab-002.html (实时测试) (来源)
- lab-003.html (实时测试) (来源)
- lab-004.html (实时测试) (来源)
- lab-005.html (实时测试) (来源)
- lab-006.html (实时测试) (来源)
- lab-007.html (实时测试) (来源)
- lab-008.html (实时测试) (来源)
- lab-l-over-100-1.html (实时测试) (来源)
- lab-l-over-100-2.html (实时测试) (来源)
- color-valid.html (实时测试) (来源)
- color-computed-lab.html (实时测试) (来源)
- color-invalid-lab.html (实时测试) (来源)
- color-valid-lab.html (实时测试) (来源)
在Lab中, 第一个参数指定了 CIE 亮度,即 L。 这是一个介于 0% 或 0 和 100% 或 100 之间的数值。 值小于 0% 或 0 时必须在解析值时夹紧为 0%; 值大于 100% 或 100 时在解析值时夹紧为 100%。
第二个和第三个参数是 Lab 色彩空间中 “a” 轴和 “b” 轴上的距离, 如前一节所述。 这些值是有符号的 (允许为正或负值), 理论上不受限 (但实际应用中真实颜色不会超过 ±160)。
有一个可选的第四个 <alpha-value> 分量, 用斜杠分隔, 表示 alpha 分量。
如果 Lab 颜色的明度(经过钳制后)为 0%, 或 100%, 那么该颜色因显示设备的色域映射 会分别显示为黑色或白色。
lab ( 29.2345 % 39.3825 20.0664 ); lab ( 52.2345 40.1645 59.9971 ); lab ( 60.2345 -5.3654 58.956 ); lab ( 62.2345 % -34.9638 47.7721 ); lab ( 67.5345 -8.6911 -41.6019 ); lab ( 29.69 % 44.888 % -29.04 % )
lch () =lch ( [ <percentage> | <number> | none] [ <percentage> | <number> | none] [ <hue> | none] [ /[ <alpha-value> | none] ] ?)
| 百分比 | 允许用于 L 和 C |
|---|---|
| 百分比参考范围 | 对于 L: 0% = 0.0, 100% = 100.0 对于 C: 0% = 0, 100% = 150 |
| 无效色相 ε | C <= 0.0015 |
测试
- lch-001.html (实时测试) (来源)
- lch-002.html (实时测试) (来源)
- lch-003.html (实时测试) (来源)
- lch-004.html (实时测试) (来源)
- lch-005.html (实时测试) (来源)
- lch-006.html (实时测试) (来源)
- lch-007.html (实时测试) (来源)
- lch-008.html (实时测试) (来源)
- lch-009.html (实时测试) (来源)
- lch-010.html (实时测试) (来源)
- lch-l-over-100-1.html (实时测试) (来源)
- lch-l-over-100-2.html (实时测试) (来源)
- color-valid.html (实时测试) (来源)
在 CIE LCH 中,第一个参数表示 CIE 的亮度 L,与 lab() 的亮度参数相同。
第二个参数是色度 C(大致表示“颜色的浓度”)。 它的最小有效值为 0,而最大值在理论上是无限的(但实际上不超过 230)。 如果提供的值为负数,则在解析值阶段将其夹紧到 0。
第三个参数是色相角 H。 它的解释方式类似于 <hue> 参数在 hsl() 中的解释方式, 但在这里色相均匀地分布在角度上,而不是与角度相同的映射。 因此,0deg 沿正 "a" 轴方向(朝向紫红色),(同样适用于 360deg、720deg 等); 90deg 沿正 "b" 轴方向(朝向芥黄色),180deg 沿负 "a" 轴方向 (朝向绿色青色),270deg 沿负 "b" 轴方向(朝向天蓝色)。
有一个可选的第四个 <alpha-value> 组件, 用斜线分隔,表示 alpha 组件。
如果 LCH 颜色的色度为 0%,则色相组件是 无效的。 如果 LCH 颜色的亮度(在夹紧后)为 0% 或 100%,则颜色将分别显示为黑色或白色, 这是由于显示器的色域映射。
lch ( 29.2345 % 44.2 27 ); lch ( 52.2345 % 72.2 56.2 ); lch ( 60.2345 59.2 95.2 ); lch ( 62.2345 % 59.2 126.2 ); lch ( 67.5345 % 42.5 258.2 ); lch ( 29.69 % 45.553 % 327.1 )
在lab或lch 中, 这些函数在此规范级别是全新的, 因此lab() 和 lch() 不 支持使用逗号分隔其所有参数的 旧颜色语法。 在这些函数中使用逗号是错误的。
9.4. 指定 Oklab 和 OKLCh:oklab() 和 oklch() 函数表示法
CSS 允许直接用 Oklab 和 OKLCh 表示颜色。
oklab () =oklab ( [ <percentage> | <number> | none] [ <percentage> | <number> | none] [ <percentage> | <number> | none] [ /[ <alpha-value> | none] ] ?)
| 百分比 | L、a 和 b 可以使用百分比 |
|---|---|
| 百分比参考范围 | 对于 L:0% = 0.0,100% = 1.0 对于 a 和 b:-100% = -0.4,100% = 0.4 |
测试
- oklab-001.html (实时测试) (来源)
- oklab-002.html (实时测试) (来源)
- oklab-003.html (实时测试) (来源)
- oklab-004.html (实时测试) (来源)
- oklab-005.html (实时测试) (来源)
- oklab-006.html (实时测试) (来源)
- oklab-007.html (实时测试) (来源)
- oklab-008.html (实时测试) (来源)
- oklab-009.html (实时测试) (来源)
- oklab-l-almost-0.html (实时测试) (来源)
- oklab-l-almost-1.html (实时测试) (来源)
- oklab-l-over-1-1.html (实时测试) (来源)
- oklab-l-over-1-2.html (实时测试) (来源)
- color-valid.html (实时测试) (来源)
在 Oklab 中,第一个参数指定 Oklab 的亮度。 这是一个在 0% 或 0 和 100% 或 1.0 之间的数字。
小于 0% 或 0.0 的值必须在解析时被钳制到 0%; 大于 100% 或 1.0 的值在解析时被钳制到 100%。
第二个和第三个参数是沿 Oklab 颜色空间中的 "a" 和 "b" 轴的距离, 如前一节所述。 这些值是有符号的 (允许正值和负值) 理论上是无限的 (但实际上不超过 ±0.5)。
还有一个可选的第四个 <alpha-value> 组件, 用斜杠分隔, 表示alpha 组件。
如果 Oklab 颜色的亮度为 0% 或 0, 或者 100% 或 1.0, 则由于显示的色域映射, 颜色将分别显示为黑色或白色。
oklab ( 40.101 % 0.1147 0.0453 ); oklab ( 59.686 % 0.1009 0.1192 ); oklab ( 0.65125 -0.0320 0.1274 ); oklab ( 66.016 % -0.1084 0.1114 ); oklab ( 72.322 % -0.0465 -0.1150 ); oklab ( 42.1 % 41 % -25 % )
oklch () =oklch ( [ <percentage> | <number> | none] [ <percentage> | <number> | none] [ <hue> | none] [ /[ <alpha-value> | none] ] ?)
| 百分比 | 适用于 L 和 C |
|---|---|
| 百分比参考范围 | 对于 L:0% = 0.0, 100% = 1.0 对于 C:0% = 0.0, 100% = 0.4 |
| 无效色相 ε | C <= 0.000004 |
测试
- oklch-001.html (实时测试) (来源)
- oklch-002.html (实时测试) (来源)
- oklch-003.html (实时测试) (来源)
- oklch-004.html (实时测试) (来源)
- oklch-005.html (实时测试) (来源)
- oklch-006.html (实时测试) (来源)
- oklch-007.html (实时测试) (来源)
- oklch-008.html (实时测试) (来源)
- oklch-009.html (实时测试) (来源)
- oklch-010.html (实时测试) (来源)
- oklch-011.html (实时测试) (来源)
- oklch-l-almost-0.html (实时测试) (来源)
- oklch-l-almost-1.html (实时测试) (来源)
- oklch-l-over-1-1.html (实时测试) (来源)
- oklch-l-over-1-2.html (实时测试) (来源)
- color-valid.html (实时测试) (来源)
在 OkLCh 中, 第一个参数指定 OkLCh 明度 L, 与 oklab() 的明度参数解释方式完全一致。
第二个参数是色度 C。 它的最小有效值是 0, 最大值理论上没有限制 (但实际上不会超过 0.5)。 如果提供的值为负数, 解析时会被钳制为 0。
第三个参数是色相角 H。 它的解释方式类似 <hue>, 以及 hsl() 和 lch() 的参数, 但色相与角度的映射方式不同。 0deg 指向正 "a" 轴(偏向紫红色), (360deg、720deg 等也如此); 90deg 指向正 "b" 轴(偏向芥末黄), 180deg 指向负 "a" 轴(偏向青绿色), 270deg 指向负 "b" 轴(偏向天蓝色)。
有一个可选的第四个 <alpha-value> 分量, 用斜杠分隔, 表示 alpha 分量。
如果 OkLCh 颜色的色度为 0% 或 0, 那么色相分量是无效分量。 如果 OkLCh 颜色的明度为 0% 或 0, 或 100% 或 1.0, 那么该颜色会因显示设备的色域映射 分别显示为黑色或白色。
oklch ( 40.101 % 0.12332 21.555 ); oklch ( 59.686 % 0.15619 49.7694 ); oklch ( 0.65125 0.13138 104.097 ); oklch ( 0.66016 0.15546 134.231 ); oklch ( 72.322 % 0.12403 247.996 ); oklch ( 42.1 % 48.25 % 328.4 )
对于 oklab 和 oklch,它们都是本规范此级新增内容, 所以 oklab() 和 oklch() 不支持用逗号分隔所有参数的传统颜色语法。 在这些函数内部使用逗号会被视为错误。
9.5. Lab 或 Oklab 颜色转换为 LCH 或 OkLCh 颜色
转换为极坐标形式非常简单:
- C = sqrt(a^2 + b^2)
- 如果 (C > epsilon) H = atan2(b, a) 否则 H 缺省
- L 保持一致
当 a 和 b 极小(色度接近零)时, 虽然视觉颜色不会发生改变,依旧位于中性轴, 但这些值稍作变动,就导致输出的色相角剧烈抖动,呈现几乎随机的结果。 在 CSS 中,这意味着该色相为无效分量, 并在转换到 LCH 或 OkLCh 时视为缺省分量; 而在非 CSS 场景下,这可能体现在输出缺省值(如 NaN)。
9.6. LCH 或 OkLCh 颜色转换为 Lab 或 Oklab 颜色
转换为直角坐标形式非常简单:
- 如果 H 缺省,则 a = b = 0
-
否则,
- a = C cos(H)
- b = C sin(H)
- L 保持一致
10. 预定义的色彩空间
CSS 提供了几种预定义的色彩空间, 包括 display-p3 [Display-P3], 这是一种典型的广色域空间,适用于当前的广色域显示器,prophoto-rgb,广泛用于摄影师, 以及 rec2020 [Rec.2020], 这是一种广播行业标准, 超广色域空间,几乎可以表示所有可见的真实世界色彩。
10.1. 指定预定义颜色:color() 函数
color() 函数允许在特定的、指定的 色彩空间 中指定颜色 (而不是大多数其他颜色函数操作的隐含 sRGB 色彩空间)。 它的语法如下:
color () =color ( <colorspace-params>[ /[ <alpha-value> | none] ] ?) <colorspace-params> =[ <predefined-rgb-params> | <xyz-params>] <predefined-rgb-params> = <predefined-rgb>[ <number> | <percentage> | none] { 3 } <predefined-rgb> = srgb | srgb-linear | display-p3 | display-p3-linear | a98-rgb | prophoto-rgb | rec2020 <xyz-params> = <xyz-space>[ <number> | <percentage> | none] { 3 } <xyz-space> = xyz | xyz-d50 | xyz-d65
测试
color 函数接受指定颜色的参数,并在显式列出的色彩空间中指定。
参数形式如下:
-
一个 <ident>,表示 预定义的色彩空间之一(例如 display-p3)。 具体的 预定义色彩空间 可能进一步限制是否可以使用 <number> 或 <percentage> 或两者。
如果 <ident> 指定的名称不是一个有效的色彩空间 (即不是 预定义色彩空间 之一), 那么该参数表示一个 无效颜色。
-
色彩空间接受的三个参数值(如 RGB 或 XYZ 值)。
超出色域的颜色具有小于 0 或 0%,或者大于 1 或 100% 的分量值。 这些并不是无效的,且会保留用于中间计算; 但是在显示时,它们会使用相对比色意图进行 CSS 色域映射, 使得值(在显示色彩空间中)在实际值时落入 0/0% 到 1/100% 的范围内。
-
一个可选的斜线分隔的 <alpha-value>。
使用 color() 没有 Web 兼容性问题, 因为它是在这个规范级别中新引入的,因此 color() 不 支持使用逗号分隔所有参数的 旧色彩语法。 在这个函数中使用逗号是错误的。
如果颜色是 无效颜色 或者 是 超出色域 的颜色, 那么该颜色 无法显示。
如果指定的颜色 可以显示, (即它不是 无效颜色 并且不是 超出色域), 那么这就是 color() 函数的实际值。
如果指定的颜色 是一个 有效颜色 但 无法显示, 那么实际值是从指定的颜色派生的,并经过 CSS 色域映射 用于显示。
在 LCH 中,该颜色为color ( rec20200.42053 0.979780 0.00579 );
在 display-p3 中,该颜色为lch ( 85.9017 % 166.116 138.207 );
且超出了 display-p3 的色域 (红色和蓝色为负值,绿色大于1)。 如果你有显示 p3 屏幕,则该颜色:color ( display-p3-0.350289 1.00707 -0.144209 );
- 有效
- 在色域范围内(对于 rec.2020)
- 超出色域范围(对于你的显示器)
- 因此 无法显示
color ( profoto-rgb0.4835 0.9167 0.2188 )
10.2. 预定义的 sRGB 颜色空间:sRGB 关键字
下面定义的 sRGB 预定义颜色空间 与用于传统 sRGB 颜色(如 rgb())的相同。
- srgb
-
srgb
[SRGB]
色彩空间接受三个数字参数,分别表示颜色的红色、绿色和蓝色分量。
色域内的颜色,其三个分量均在 [0, 1] 范围内。
白点是 D65。
[SRGB] 指定了两种观看条件:编码和典型。[ICC] 建议使用编码条件进行颜色转换和最佳观看,这些值如下表所示。
sRGB 是 CSS 的默认色彩空间,用于所有旧版颜色函数。
它具有以下特性:
x y 红色色度 0.640 0.330 绿色色度 0.300 0.600 蓝色色度 0.150 0.060 白色色度 D65 传递函数 见下文 白色亮度 80.0 cd/m2 黑色亮度 0.20 cd/m2 图像状态 显示参考 百分比 允许用于 R、G 和 B 百分比参考范围 对于 R,G,B:0% = 0.0,100% = 1.0
c 是经过伽马编码的红色、绿色或蓝色分量。 cl 是相应的线性光分量。let sign= c< 0 ? - 1 : 1 ; let abs= Math. abs( c); if ( abs<= 0.04045 ) { cl= c/ 12.92 ; } else { cl= sign* ( Math. pow(( abs+ 0.055 ) / 1.055 , 2.4 )); } Oklch 中 sRGB 色彩空间的可视化。图中显示了原色和间色。
测试
10.3. 预定义的线性光 sRGB 颜色空间:srgb-linear 关键字
sRGB-linear 预定义颜色空间 与 srgb 相同,除了传递函数为线性光(无伽马编码)。
- srgb-linear
-
srgb-linear [SRGB]
色彩空间接受三个数字参数,
分别表示颜色的红色、绿色和蓝色分量。
色域内的颜色,其三个分量均在 [0, 1] 范围内。
白点是 D65。
它具有以下特性:
x y 红色色度 0.640 0.330 绿色色度 0.300 0.600 蓝色色度 0.150 0.060 白色色度 D65 传递函数 统一,见下文 白色亮度 80.0 cd/m2 黑色亮度 0.20 cd/m2 图像状态 显示参考 百分比 允许用于 R, G 和 B 百分比参考范围 对于 R, G, B: 0% = 0.0, 100% = 1.0 cl
c 是红、绿或蓝分量。 cl 是相应的线性光分量,且与 c 相同。= c; 为避免带状伪影,需要更高的精度用于 srgb-linear 相较于 srgb。
测试
10.4. 预定义的 Display P3 颜色空间:display-p3 关键字
- display-p3
-
display-p3 [Display-P3] 色彩空间接受三个数字参数,
分别表示颜色的红色、绿色和蓝色分量。
色域内的颜色,其三个分量均在 [0, 1] 范围内。
它使用与 [DCI-P3] 相同的主色度,
但使用 D65 白点,以及与 sRGB 相同的传递曲线。
现代显示器、电视、笔记本屏幕和手机屏幕能够显示全部或几乎全部的 display-p3 色域。
它具有以下特性:
x y 红色色度 0.680 0.320 绿色色度 0.265 0.690 蓝色色度 0.150 0.060 白色色度 D65 传递函数 与 srgb 相同 白色亮度 80.0 cd/m2 黑色亮度 0.80 cd/m2 图像状态 显示参考 百分比 允许用于 R, G 和 B 百分比参考范围 对于 R, G, B: 0% = 0.0, 100% = 1.0 Oklch 中 P3 色彩空间的可视化。 图中显示了原色和间色 (但使用 sRGB 呈现,并非正确的颜色)。 为便于比较,图中还以虚线圆圈 显示了 sRGB 的原色和间色。 P3 原色具有更高的彩度。
测试
10.5. 预定义线性光 Display P3 色彩空间:display-p3-linear 关键字
- display-p3-linear
-
display-p3-linear 预定义色彩空间
与 display-p3 相同,
除了其传递函数
是线性光(没有伽马编码)。
它具有以下特性:
x y 红色度 0.680 0.320 绿色度 0.265 0.690 蓝色度 0.150 0.060 白色色度 D65 传递函数 恒等,见下文 白色亮度 80.0 cd/m2 黑色亮度 0.80 cd/m2 图像状态 显示参考 百分比 允许用于 R、G 和 B 百分比参考范围 对于 R、G、B:0% = 0.0,100% = 1.0 cl
= c; c 是红色、绿色或蓝色分量。 cl 是对应的线性光分量,二者相同。
为避免出现色带伪影, 需要更高的精度 才能用于 display-p3-linear,相比 display-p3 而言。
测试
10.6. 预定义 A98 RGB 色彩空间:a98-rgb 关键字
- a98-rgb
-
a98-rgb 色彩空间接受三个数值参数,
表示颜色的红、绿、蓝分量。
色域范围内的颜色,其三个分量都在 [0, 1] 之间。
变换曲线是
接近但不等于 1/2.2 的伽玛函数。
其具有以下特性:
x y 红色色度 0.6400 0.3300 绿色色度 0.2100 0.7100 蓝色色度 0.1500 0.0600 白色色度 D65 变换函数 256/563 白色亮度 160.0 cd/m2 黑色亮度 0.5557 cd/m2 图像状态 显示参考 百分比 R、G 和 B 都允许 百分比参考范围 对于 R、G、B:0% = 0.0,100% = 1.0 Oklch 中 A98 色彩空间的可视化。 图中显示了原色和间色 (但使用 sRGB 呈现,并非正确的颜色)。 为便于比较,图中还以虚线圆圈 显示了 sRGB 的原色和间色。 a98 原色具有更高的彩度, 尤其是黄色、绿色和青色。
测试
10.7. 预定义 ProPhoto RGB 色彩空间:prophoto-rgb 关键字
- prophoto-rgb
-
prophoto-rgb 色彩空间接受三个数字参数,
分别表示颜色的红色、绿色和蓝色分量。
色域内的颜色,其三个分量均在 [0, 1] 范围内。
传递曲线是一个伽马值为 1/1.8 的伽马函数,
在黑色附近有一个小的线性部分。
白点是 D50,与 CIE Lab 使用的白点相同。因此,
转换为 CIE Lab 不需要色度适应步骤。
ProPhoto RGB 空间使用超饱和的、非物理可实现的原色。 这些原色的选择旨在提供广色域,特别是为了在色调操作下最小化色相偏移。 它通常用于数字摄影作为宽色域的颜色空间, 用于保存摄影图像的存档版本。 prophoto-rgb 颜色空间允许 CSS 指定与这些图像中 RGB 值相同的颜色。
ProPhoto RGB 空间最初由柯达开发,并在 [Wolfe] 中进行了描述。 它由 ISO 标准化为 [ROMM],[ROMM-RGB]。
白色亮度给出一个范围,视场耀斑(也就是黑色亮度)是此范围的 0.5% 到 1.0%。
它具有以下特性:
x y 红色色度 0.734699 0.265301 绿色色度 0.159597 0.840403 蓝色色度 0.036598 0.000105 白色色度 D50 传递函数 见下文 白色亮度 160.0 到 640.0 cd/m2 黑色亮度 见文本 图像状态 显示参考 百分比 允许用于 R, G 和 B 百分比参考范围 对于 R, G, B: 0% = 0.0, 100% = 1.0
c 是伽玛编码的红、绿或蓝分量。 cl 是相应的线性光分量。const E= 16 / 512 ; let sign= c< 0 ? - 1 : 1 ; let abs= Math. abs( c); if ( abs<= E) { cl= c/ 16 ; } else { cl= sign* Math. pow( c, 1.8 ); } Oklch 中 prophoto-rgb 色彩空间的可视化。图中显示了原色和间色 (但使用 sRGB 呈现,并非正确的颜色)。 为便于比较,图中还以虚线圆圈 显示了 sRGB 的原色和间色。 prophoto-rgb 的原色和间色具有高得多的彩度, 但这一超宽色域中的很大一部分 并不对应物理上可实现的颜色。
测试
10.8. 预定义的 ITU-R BT.2020-2 颜色空间:rec2020 关键字
- rec2020
-
rec2020 [Rec.2020]
色彩空间接受三个数值参数,
分别代表颜色的红、绿、蓝分量。
色域内的颜色这三个分量都在 [0, 1] 范围内,
(在视频术语中称为“满范围”)。
ITU 参考 2020 用于
超高清、4k 和 8k 电视。
主色可以物理实现, 但难度很高, 因为它们非常接近光谱曲线边界。
目前的显示器无法还原 rec2020 的全部色域。 随着显示技术提升,覆盖率未来会逐步增加。
其具有以下特性:
x y 红色色度 0.708 0.292 绿色色度 0.170 0.797 蓝色色度 0.131 0.046 白色色度 D65 变换函数 伽玛 2.40,参考 [REC_BT.1886] 图像状态 显示参考 百分比 R、G 和 B 都允许 百分比参考范围 R、G、B:0% = 0.0,100% = 1.0 Oklch 中 rec2020 色彩空间的可视化。图中显示了原色和间色 (但使用 sRGB 呈现,并非正确的颜色)。 为便于比较,图中还以虚线圆圈 显示了 sRGB 的原色和间色。 rec2020 原色具有高得多的彩度。
测试
10.9. 预定义的 CIE XYZ 颜色空间:xyz-d50、xyz-d65 和 xyz 关键字
- xyz-d50、xyz-d65、xyz
-
xyz
色彩空间接受三个数字参数,
分别表示 X、Y 和 Z 值。
它表示 CIE XYZ [COLORIMETRY] 色彩空间,
经过缩放以使漫射白的亮度 (Y) 为 1.0,
并且在必要时进行色度适应以匹配参考白。
xyz-d50 的参考白是 D50,而 xyz-d65 和 xyz 的参考白是 D65。
允许大于 1.0/100% 的值,并且不得被限制; Y 大于 1.0 的颜色表示比漫射白更亮的颜色。 小于 0/0% 的值不常见, 但可能由于色度适应而出现, 同样不得被限制。
它具有以下特性:
百分比 允许用于 X, Y, Z 百分比参考范围 对于 X, Y, Z: 0% = 0.0, 100% = 1.0
测试
- predefined-016.html (实时测试) (来源)
- xyz-001.html (实时测试) (来源)
- xyz-002.html (实时测试) (来源)
- xyz-003.html (实时测试) (来源)
- xyz-004.html (实时测试) (来源)
- xyz-005.html (实时测试) (来源)
- xyz-d50-001.html (实时测试) (来源)
- xyz-d50-002.html (实时测试) (来源)
- xyz-d50-003.html (实时测试) (来源)
- xyz-d50-004.html (实时测试) (来源)
- xyz-d50-005.html (实时测试) (来源)
- xyz-d65-001.html (实时测试) (来源)
- xyz-d65-002.html (实时测试) (来源)
- xyz-d65-003.html (实时测试) (来源)
- xyz-d65-004.html (实时测试) (来源)
- xyz-d65-005.html (实时测试) (来源)
- color-valid.html (实时测试) (来源)
10.10. 将预定义的颜色空间转换为 Lab 或 Oklab
对于所有预定义的 RGB 颜色空间,转换为 Lab 需要几个步骤,虽然在实践中,除第一个步骤外,其余步骤都是线性计算,并且可以组合在一起。
- 将伽玛编码的 RGB 转换为线性光 RGB(撤销伽玛编码)
- 将线性 RGB 转换为 CIE XYZ
- 如需,将 D65 白点 (用于 sRGB、display-p3、a98-rgb 以及 rec2020) 转换为 Lab 所用的 D50 白点, 使用线性 Bradford 变换。prophoto-rgb 已采用 D50 白点。
- 将 D50 适配的 XYZ 转换为 Lab
转换为 Oklab 类似, 但色度适配步骤 只需要对 prophoto-rgb 进行。
- 将伽玛编码的 RGB 转换为线性光 RGB(撤销伽玛编码)
- 将线性 RGB 转换为 CIE XYZ
- 如需,将 D50 白点(用于 prophoto-rgb) 转换为 Oklab 所用的 D65 白点, 使用线性 Bradford 变换。
- 将 D65 适配的 XYZ 转换为 Oklab
这些转换的 JavaScript 示例代码可以在 § 19 颜色转换的示例代码 中找到。
10.11. 将 Lab 或 Oklab 转换为预定义的 RGB 颜色空间
从 Lab 转换为诸如 display-p3 或 rec2020 等预定义空间也需要多个步骤,并且在实践中,除最后一步外,其余步骤都是线性计算,并且可以组合在一起。
- 将 Lab 转换为(D50 适应的)XYZ
- 如果需要,使用线性 Bradford 变换将 Lab 使用的 D50 白点转换为 sRGB 和大多数其他 RGB 空间中使用的 D65 白点。 prophoto-rgb 不需要此步骤。
- 将(D65 适应的)CIE XYZ 转换为线性 RGB
- 将线性光 RGB 转换为 RGB(执行伽马编码)
从 Oklab 转换类似,但色度适应步骤仅对 prophoto-rgb 需要。
- 将 Oklab 转换为(D65 适应的)XYZ
- 如果需要,使用线性 Bradford 变换将 Oklab 使用的 D65 白点转换为 prophoto-rgb 中使用的 D50 白点。
- 将(D65 适应的)CIE XYZ 转换为线性 RGB
- 将线性光 RGB 转换为 RGB(执行伽马编码)
这些转换的 JavaScript 示例代码可以在 § 19 颜色转换的示例代码 中找到。
实现可能选择以其他方式实现这些步骤(例如,使用具有相对色度渲染意图的 ICC 配置文件),只要源和目标色域中的颜色的结果相同即可。
10.12. 在预定义 RGB 颜色空间之间转换
从一个预定义的 RGB 颜色空间转换到另一个需要多个步骤,其中一个步骤仅在白点不同的情况下需要。要从 src 转换到 dest:
- 从伽马编码的 srcRGB 转换为线性光 srcRGB(撤销伽马编码)
- 将线性 srcRGB 转换为 CIE XYZ
- 如果 src 和 dest 具有不同的白点,则将 XYZ 值从 srcWhite 转换为 destWhite,使用线性 Bradford 转换。
- 将 CIE XYZ 转换为线性 destRGB
- 将线性光 destRGB 转换为 destRGB(进行伽马编码)
这些预定义 RGB 颜色空间的转换的 JavaScript 示例代码可以在 § 19 颜色转换的示例代码 中找到。
10.13. 简单的 Alpha 合成
绘制时,实施必须根据 《合成与混合》第 5.1 节“简单 alpha 合成”中的规则处理 alpha 值 [Compositing]。
11. 转换颜色
测试
本节提供的算法在后面使用,不需要测试。
11.1. 颜色转换简介
颜色可以 从一个颜色空间转换到另一个颜色空间,并且, 如果不进行色域映射 且每个颜色空间都能够表示超出色域的颜色, (对于 RGB 空间,这意味着传递函数定义在扩展范围内) 那么 (受数值精度和舍入误差影响) 这两种颜色看起来将相同,并表示相同的色觉。
- oklch(65% 0.15 270)
- lab(57.9% 11.4 -53.7)
- color(display-p3 0.445 0.529 0.891)
- color(rec2020 0.522 0.557 0.892)
- #6c88ea
由于历史原因,在解析时, 某些旧式颜色 语法的颜色会被钳制。 不过一般而言,这种钳制并不可取, 因为它会干扰往返转换, 并导致误差在多次颜色转换中累积。
因此,颜色转换的结果不会被钳制, 这可能产生超出色域的颜色, 或者甚至产生虚构颜色, 例如位于光谱轨迹之外的颜色, 甚至是明度为负值 或明度大于允许的最大值的颜色。
- oklch(65% 0.25 270)
- lab(56.03% 32.3 -88.58)
- color(display-p3 0.3731 0.4673 1.105)
- color(rec2020 0.4929 0.5062 1.094)
- color(srgb 0.3475 0.4707 1.146)
- color(xyz-d65 1 1 1)
- lab(100.1154% 9.064489 5.801761)
11.2. 颜色转换算法
- 为转换准备 col1
- 将任何缺失分量替换为零。
- 如果 src 不是 线性光表示, 则将其转换为线性光 (撤销伽马编码) 并将其作为新的 col1。
- 将 col1 转换为 CIE XYZ, 使用给定的白点 src-white 并将其作为 xyz。
- 如果 dest-white 与 src-white 不相同, 则使用线性 Bradford 色适应变换 对 xyz 进行色适应,使其适应 dest-white, 并将其作为新的 xyz。
- 如果 dest 是圆柱极坐标颜色表示, 则令 dest-rect 为相应的直角 正交颜色表示。 否则,令 dest-rect 为 dest。
- 将 xyz 转换为 dest, 然后应用任何传递函数 (伽马编码), 生成 col2。
- 如果 dest 是 物理输出颜色空间,例如显示器, 则 col2 必须进行 CSS 色域映射, 使其可以 显示。
- 如果 dest-rect 与 dest 不相同, 换言之,dest 是圆柱极坐标颜色表示, 则从 dest-rect 转换为 dest,并将其作为 col2。 这可能产生缺失分量。
12. 比较 <color> 值
当两个 <color> 值按下述算法比较结果为相等时, 它们是 等价颜色。 例如, 这种比较用于 style() 容器查询 [CSS-CONDITIONAL-5] 以及 CSS 过渡 [CSS-TRANSITIONS-1] ,以确定颜色值是否发生变化。
给定两个 <color> 值 C1 和 C2, 当且仅当以下算法返回 true 时, 它们是 等价颜色:
- 对于 C1 和 C2 中的每一个, 将任何无效分量转换为缺失分量。
-
如果 C1 和 C2 具有相同的 <color-space>:
- 逐一比较它们的分量, 包括 alpha 通道。 缺失分量仅与 另一个缺失分量相等。 如果两个数值分量之间的差值不超过一个很小的 实现定义的 ε, 则认为它们相等。
- 当且仅当 所有分量比较结果均为相等时,返回 true。
- 否则,C1 和 C2 位于不同的 <color-space> 中。 如果任一颜色至少有一个缺失分量, 则返回 false。
- 否则,这两个颜色都没有任何缺失分量。 将 C1 和 C2 都转换为 oklab, 然后,当且仅当转换后颜色的 所有分量(包括 alpha)比较结果均为相等时, 返回 true, 并使用标准化的 Oklab ε 值 0.00001。
注: 两种以不同 语法形式表示的颜色, 位于同一 <color-space> 中, 但在色度学上完全相同——例如, red 和 color(srgb 1 0 0)——根据此算法的第 2 步,是等效颜色。
注: 两种以不同 <color-space> 表示 但在色度学上完全相同的颜色——例如, red 和 color(display-p3 0.91748756 0.20028681 0.13856059)——根据此算法的第 4 步,是等效颜色, 因为它们会转换为相同的 oklab 值。
就此比较而言, rgb()、rgba()、hsl()、hsla()、hwb()、 十六进制颜色、命名颜色以及 系统颜色 都被视为处于 srgb <color-space> 中。
13. 颜色插值
颜色插值发生在 渐变、 合成、 滤镜、 过渡、 动画,以及 颜色混合和颜色修改函数中。
两个 <color> 值之间的插值 通过执行以下步骤进行:
- 检查这两种颜色的对应分量 和对应集合, 它们将被向前 传递
- (如果需要转换)将任何无作用的分量更改为缺失值
- (如果需要)将两者转换到给定的颜色空间, 该颜色空间在下文中称为插值颜色空间。
- (如果需要)将向前传递的值重新插入转换后的颜色中
- 使用另一种颜色的分量值填充缺失分量
- (如果需要)根据所选的 <hue-interpolation-method> 修正色相
- 将颜色分量更改为预乘形式
- 分别对颜色计算值的每个分量进行线性插值
- 撤销预乘
可以对 currentcolor 进行插值或从其进行插值。 为此使用的数值是使用值。
13.1. 分量线性插值
有两种方式可以理解每个颜色分量对最终结果贡献了多少: 颜色量和颜色进度。 两者会得到相同的结果;它们只是不同的思维模型。
- 颜色量
- 给定一对百分比量 Qa 和 Qb, 它们已被归一化,使其总和为 100%, 以及一对颜色分量值 Ca 和 Cb, 插值结果为 (Ca × Qa) + ( Cb × Qb)。
- 颜色进度
- 给定一个百分比进度 P 以及起始和结束分量值 Cs 和 Ce, 插值结果为 (Cs × (1 - P) + Ce × P)。
因此,当从一种模型转换为另一种模型时,进度就是第二种颜色的量。
13.2. 插值所用的颜色空间
CSS 中的多种功能依赖于颜色插值。
要进行插值,颜色必须位于(或转换到)同一个颜色空间。
混合或以其他方式组合颜色 会根据所使用的插值颜色空间而产生不同的结果。 因此,对于各种插值使用场景,不同的颜色空间可能更加合适。
-
在某些情况下,希望得到两种有色光进行物理混合的结果。 在这种情况下,CIE XYZ、display-p3-linear 或 srgb-linear 颜色空间是合适的,因为 它们与光强度呈线性关系。
-
如果颜色在感知上需要均匀间隔(例如在渐变中), Oklab 颜色空间(以及程度稍逊的较旧 Lab), 就是为实现感知均匀性而设计的。
-
最后,与旧版 Web 内容的兼容性可能是最重要的考虑因素。 sRGB 颜色空间既不是线性光颜色空间,也不是 感知均匀颜色空间,但在这里会选择它, 即使它会产生较差的结果(混合结果过暗或偏灰)。
这些功能统称为宿主语法。
为了允许宿主语法指示插值颜色空间, 本规范导出了 color-interpolation-method 产生式。 本规范本身并不使用它, 只是将其公开以便其他规范使用; 例如,参见 CSS Images 4 § 3.1 线性 渐变:linear-gradient() 表示法中的用法。
宿主语法应定义 每种情况下默认的插值颜色空间应是什么, 并且最好提供语法供作者覆盖这一默认值。 如果这种语法是属性值的一部分,它应使用 color-interpolation-method 产生式, 该产生式在下方定义,以便其他规范引用。 这可确保整个 CSS 保持一致, 并使以后对颜色插值执行方式的进一步定制 能够自动传播到整个 CSS。
<color-space> = <rectangular-color-space> | <polar-color-space> <rectangular-color-space> = srgb | srgb-linear | display-p3 | display-p3-linear | a98-rgb | prophoto-rgb | rec2020 | lab | oklab | <xyz-space> <polar-color-space> = hsl | hwb | lch | oklch <hue-interpolation-method> =[ shorter | longer | increasing | decreasing] hue <color-interpolation-method> = in[ <rectangular-color-space> | <polar-color-space> <hue-interpolation-method>?]
<rectangular-color-space> 和 <polar-color-space> 定义中的 每个关键字都指其对应的颜色空间, 在 CSS 中,要么由同名的函数语法表示, 要么(如果不存在这样的函数)由 color() 函数中对应的 <ident> 表示。
测试
- gradients-with-transparent.html (实时 测试) (源码)
- gradient-eval-001.html (实时 测试) (源码)
- gradient-eval-002.html (实时 测试) (源码)
- gradient-eval-003.html (实时 测试) (源码)
- gradient-eval-004.html (实时 测试) (源码)
- gradient-eval-005.html (实时 测试) (源码)
- gradient-eval-006.html (实时 测试) (源码)
- gradient-eval-007.html (实时 测试) (源码)
- gradient-eval-008.html (实时 测试) (源码)
- gradient-eval-009.html (实时 测试) (源码)
- gradient-none-interpolation.html (实时 测试) (源码)
- oklab-gradient.html (实时 测试) (源码)
- srgb-gradient.html (实时测试) (源码)
- srgb-linear-gradient.html (实时 测试) (源码)
- xyz-gradient.html (实时测试) (源码)
- gradient-interpolation-method-valid.html (实时 测试) (源码)
- gradient-interpolation-method-invalid.html (实时 测试) (源码)
- gradient-interpolation-method-computed.html (实时 测试) (源码)
如果宿主语法没有定义应在哪个颜色空间中 进行插值, 则默认为 Oklab。
对于 <polar-color-space>, 如果没有指定 <hue-interpolation-method>, 则默认为 shorter。
但是,用户代理必须在伽马编码的 sRGB 空间中处理 旧版 sRGB 颜色格式之间的插值 (十六进制颜色、命名颜色、 rgb()、hsl() 或 hwb() 以及包含 alpha 的等效形式)。 这提供了 Web 兼容性; 旧版 sRGB 内容默认在 sRGB 空间中进行插值。
这也意味着作者可以选择 使用更好的插值方式, 即使是在 sRGB 颜色之间, 只需对其中至少一种颜色使用非旧版的 color(srgb r g b) 形式, 或者显式指定插值颜色空间。
如果要插值的颜色位于 插值颜色空间的色域之外, 那么一旦转换到该空间, 它们将包含超出范围的值。
这些值不会被裁剪;必须按原值进行插值。
13.3. 缺失分量的插值
在将这两种颜色 转换到插值颜色空间的过程中, 任何缺失 分量 都会被值 0 替换。
因此,对两种颜色进行插值的第一阶段 是对输入颜色中的任何缺失分量 进行分类, 并将它们与 插值颜色空间的分量进行比较。 如果发现任何属于缺失分量的对应 分量, 它们将被向前传递 并重新插入转换后的颜色中, 此操作发生在预乘之前,以及 线性插值发生之前。
类似地,如果原始颜色中 一个对应集合 (定义见下文) 的每个分量 都是一个缺失 分量, 那么它们都会被向前 传递 并重新插入 插值颜色空间中对应的对应集合。
Alpha 本身就是其自身的对应分量 (alpha 与 alpha 对应), 因此缺失的 alpha 会以与任何其他缺失分量完全相同的方式被向前传递。
对应 分量如下:
| 类别 | 分量 |
|---|---|
| 红色 | r,x |
| 绿色 | g,y |
| 蓝色 | b,z |
| 亮度 | L |
| 色彩鲜艳度 | C, S |
| 色相 | H |
| 对立 a | a |
| 对立 b | b |
| Alpha | alpha |
注: 就此分类而言, XYZ 空间被视为超饱和的 RGB 空间。 此外,尽管饱和度依赖于亮度, 但在这里它与色度归入同一类别。 HWB 的白度和黑度分量 在其他颜色空间中没有对应项。
此外,对于任意两个颜色空间, 移除所有对应分量后 剩余的分量 构成一个分量的对应集合。
注: 因为所有颜色分量的完整集合 就是在不存在任何单独的对应分量时 所剩余的对应集合, 所有颜色分量均为缺失的颜色, 在插值颜色空间中也会使所有颜色分量缺失。
lab ( 50 % none none)
转换为 LCH
以进行插值时,
亮度单独是对应的。
剩余的分量
(Lab 中的 a 和 b;LCH 中的 C 和 H)
构成一个对应集合。
由于 a 和 b 都是缺失的,
因此 C 和 H 都会以缺失状态向前传递,
得到 lch ( 50 % none none)
而不是 lch ( 50 % 0 0 ) 。
类似地,
为进行插值而转换为 OKLab 时,
会得到 ,
因为这三个颜色分量
构成对应集合
(sRGB 和 OKLab 之间不存在单独的对应分量)。
lch ( 50 % 0.02 none) color ( display-p30.7 0.5 none)
它们转换为
oklch ( 56.897 % 0.0001 0 ) oklch ( 63.612 % 0.1522 78.748 )
并重新插入向前传递的缺失分量后, 要进行插值的两种颜色为:
oklch ( 56.897 % 0.0001 none) oklch ( 63.612 % 0.1522 78.748 )
如果具有向前传递的缺失分量的颜色 与另一种 不缺失该分量的颜色 进行插值, 则该缺失分量 被视为具有 另一种颜色的分量值。
因此, 必须在进行任何 无作用 分量处理之前 识别要向前传递的分量; 以防止该值在转换时变为零。
oklch ( 78.3 % 0.108 326.5 ) oklch ( 39.2 % 0.4 none)
那么实际要进行插值的颜色为
oklch ( 78.3 % 0.108 326.5 ) oklch ( 39.2 % 0.4 326.5 )
而不是
oklch ( 78.3 % 0.108 326.5 ) oklch ( 39.2 % 0.4 0 )
如果向前传递的缺失分量 是 alpha,则必须使用这个向前传递的值对颜色进行预乘, 而不是使用颜色转换原本会产生的零值。
oklch ( 0.783 0.108 326.5 /0.5 ) oklch ( 0.392 0.4 0 / none)
那么实际要进行插值的颜色为
oklch ( 78.3 % 0.108 326.5 /0.5 ) oklch ( 39.2 % 0.4 0 /0.5 )
得到预乘后的 OkLCh 值 [0.3915, 0.054, 326] 和 [0.196, 0.2, 0]。
如果两种颜色的某个给定分量 都是缺失的, 那么插值后的颜色 也会缺失该分量。
13.4. Alpha 插值
当要插值的颜色并非完全不透明时, 首先按以下方式对它们进行预乘:
-
如果 alpha 值为 none,则预乘值就是未预乘值。 否则,
-
如果任何分量值为 none,则预乘值也为 none。
-
对于矩形正交颜色坐标系, 所有分量值都乘以 alpha 值。
-
对于圆柱极坐标颜色坐标系,色相 角不进行预乘,但另外两个轴要进行预乘。
要从预乘颜色值中获得颜色值,
-
如果插值后的 alpha 值为零或 none, 则未预乘值就是预乘值。否则,
-
如果任何分量值为 none, 则未预乘值也为 none。
-
否则,将此前进行过预乘的每个分量 除以插值后的 alpha 值。
为什么预乘 alpha 很有用?
使用预乘表示进行颜色插值 往往比使用非预乘表示产生更美观的过渡, 尤其是在从完全不透明的颜色过渡到完全透明时。
请注意,如果透明度或颜色中的任一个保持不变,
这类过渡
(例如,在
(不透明红色)
与
(不透明蓝色)之间过渡,
或在
(不透明红色)
与
(透明红色)之间过渡)
无论颜色插值是在预乘还是非预乘
颜色空间中完成,结果都是相同的。
只有当两个端点之间的颜色和透明度都不同时才会出现差异。
linear-gradient ( 90 deg , red, transparent, blue)
使用预乘颜色时, 到或从 "transparent" 的过渡始终看起来很好:
另一方面, 如果渐变错误地在非预乘空间中进行过渡, 渐变中心会呈现明显的灰色, 因为 "transparent" 实际上是 rgba(0,0,0,0) 的简写,即 透明黑色, 这意味着红色会随着不透明度降低 而过渡到黑色, 蓝色的过渡也同样如此:
对这些颜色进行线性插值的中点 为 [23.4% 10.2% 38.8%], 其 alpha 值为 0.5, 撤销预乘后为 rgb(46.8% 20.4% 77.6% / 0.5)。
对这些值进行线性插值的中点为 [29.4365% 25.776 3.554], 其 alpha 值为 0.5, 撤销预乘后为 lab(58.873% 51.552 7.108) / 0.5)。
沿 shorter 色相弧(默认值) 对这些值进行线性插值的中点为 [29.4365% 40.563 31.82], 其 alpha 值为 0.5, 撤销预乘后为 lch(58.873% 81.126 31.82) / 0.5)。
§ 19 颜色转换示例代码中 提供了用于 alpha 预乘和撤销预乘的 JavaScript 示例代码, 同时适用于极坐标和矩形颜色空间。
13.5. 色相插值
对于具有色相角的颜色函数(LCH、HSL、HWB 等),有多种插值方式。 由于大于 360° 的弧通常并不可取, 因此会在插值之前调整色相角, 使逐分量插值在小于 360°、通常小于 180° 的范围内进行。
宿主语法可以指定以下任一色相插值算法 (以下角度以度为单位,但无论采用何种方式指定角度,其逻辑都相同)。 通过 <hue-interpolation-method> 标记指定色相插值策略 已经是 <color-interpolation-method> 语法的一部分。
除非另有规定,如果宿主语法没有选择特定的色相插值算法, 则默认为 shorter。
注: 提醒一下, 如果进行插值的颜色尚未处于指定的插值颜色空间中, 那么对它们进行转换会将任何无作用分量变为缺失分量。
13.5.1. shorter
色相角进行插值时,会采用起始色相与结束色相之间两条弧中较短的一条。
例如,在 OkLCh 中从红色 oklch(0.6 0.24 30) 插值到黄色 oklch(0.8 0.15 90) 时, 中点的色相角为 30 + (90 - 30) * 0.5 = 60 度, 沿两种颜色之间较短的弧, 得到深橙色 oklch(0.7 0.195 60)
调整角度,使 θ₂ - θ₁ ∈ [-180, 180]。使用伪 JavaScript 表示:
if( θ₂ - θ₁ >180 ) { θ₁ +=360 ; } else if( θ₂ - θ₁ <-180 ) { θ₂ +=360 ; }
13.5.2. longer
色相角进行插值时,会采用起始色相与结束色相之间两条弧中较长的一条。
例如,在 OkLCh 中从红色 oklch(0.6 0.24 30) 插值到黄色 oklch(0.8 0.15 90) 时, 中点的色相角为 (30 + 360 + 90) * 0.5 = 240 度, 沿两种颜色之间较长的弧, 得到天蓝色 oklch(0.7 0.195 240)
调整角度,使 θ₂ - θ₁ ∈ {(-360, -180], [180, 360)}。使用伪 JavaScript 表示:
if( 0 < θ₂ - θ₁ <180 ) { θ₁ +=360 ; } else if( -180 < θ₂ - θ₁ <=0 ) { θ₂ +=360 ; }
13.5.3. increasing
对色相角进行插值时, 随着从第一种颜色向第二种颜色推进, 角度始终增大。 如果角度增大到 360,则会重置为零, 然后继续增大。
根据两个角度之间的差值, 其效果会与 shorter 或 longer 之一相同。 但是,如果其中一个色相角正在进行动画, 并且色相角差经过 180 度, 插值不会翻转到另一条弧。
例如,在 OkLCh 中从深棕色 oklch(0.5 0.1 30) 插值到蓝绿色 oklch(0.7 0.1 190) 时, 中点的色相角为 (30 + 190) * 0.5 = 110 度, 得到卡其色 oklch(0.6 0.1 110)。
但是,如果将第二种颜色的色相动画到 oklch(0.7 0.1 230), 则插值中点为 (30 + 230) * 0.5 = 130 度, 继续沿相同的增大方向, 得到另一种绿色 oklch(0.6 0.1 130), 而不会在动画进行到一半时翻转到对立颜色。
调整角度,使 θ₂ - θ₁ ∈ [0, 360)。使用伪 JavaScript 表示:
if( θ₂ < θ₁) { θ₂ +=360 ; }
13.5.4. decreasing
对色相角进行插值时, 随着从第一种颜色向第二种颜色推进, 角度始终减小。 如果角度减小到 0,则会重置为 360, 然后继续减小。
根据两个角度之间的差值, 其效果会与 shorter 或 longer 之一相同。 但是,如果其中一个色相角正在进行动画, 并且色相角差经过 180 度, 插值不会翻转到另一条弧。
例如,在 OkLCh 中从深棕色 oklch(0.5 0.1 30) 插值到蓝绿色 oklch(0.7 0.1 190) 时, 中点的色相角为 (30 + 360 + 190) * 0.5 = 290 度, 得到紫色 oklch(0.6 0.1 290)。
但是,如果将第二种颜色的色相动画到 oklch(0.7 0.1 230), 则插值中点为 (30 + 360 + 230) * 0.5 = 310 度, 继续沿相同的减小方向, 得到另一种紫色 oklch(0.6 0.1 310), 而不会在动画进行到一半时翻转到对立颜色。
调整角度,使 θ₂ - θ₁ ∈ (-360, 0]。使用伪 JavaScript 表示:
if( θ₁ < θ₂) { θ₁ +=360 ; }
14. 色域映射
14.1. 色域映射简介
注意: 本节为文档其他部分 所述具体要求提供重要背景。
本节不具规范性
测试
本节不是规范要求,无需测试。
当一个原色空间的颜色 被转换到另一个色域更小的目标色空间时, 某些颜色值会超出目标色域。
对于中间色彩计算, 这些超出色域的值会被保留。 但如果目标是显示设备 (屏幕或打印机), 则必须把超出色域的值转换成 色域内的颜色。
色域映射就是在视觉效果尽量不显著变化的前提下, 找到一个色域内的颜色的过程。
有些超出色域的颜色对应真实世界的颜色 (理论上可物理呈现), 另一些则是虚拟色彩 (它们超出了光谱包络线, 需要超过 100% 的单一波长能量) 永远无法物理实现。 这种颜色通常由计算产生, 例如“令此颜色饱和度提高 100 倍”。
这四个角中有三个超出了光谱包络线, 对应虚拟色彩。
14.1.1. 裁剪
最简单但最不可取的方法 就是直接将分量值 剪裁到可显示范围。
由于这种方法的动力就是速度, 剪裁通常在伽玛编码值上完成, 而不是转换到线性光。
这会改变 三个基色(对 RGB 显示来说)的比例, 导致色相偏移。
color ( srgb-linear 0.5 1 3 ) 。
由于这是线性光色彩空间,
我们可以对比三个分量的强度,
得到
蓝光是绿光的三倍,
红光是绿光的一半。
蓝色基色是红色基色的六倍。
在 OkLCh 中,此色相角 265.1°
如果现在将此颜色
剪裁到 sRGB 色域,
得到 。
这时蓝光量与绿光一致。
在 OkLCh 中,该色相角 196.1°,
变化高达 69°。
如果颜色并非严重超出色域, 剪裁还能带来可接受的效果。 尤其是对较暗或为负的 分量值。
color ( rec2020 0.54 0.9 0 )
即 oklch ( 80.72 % 0.3296 141.6 ) 。
转换到 p3 色彩空间时,负值的蓝色分量表明
该颜色已经超出色域范围:
在线性光下为
。
对 gamma 编码的 p3 颜色做 gamut 剪裁得到
在线性光下为
对比得
。
这是个不错的结果; 色相角和明度几乎没变, 但色度有所降低,属于预期范围。
就线性光红绿蓝百分比而言, 红绿完全一样, 而蓝色高出 -1.46%。
color ( prophoto-rgb 0.2 1.0 0.1 )
即 oklch ( 85.07 % 0.4873 151.4 )
转换到 p3 色彩空间后,该颜色明显
超出色域:
在线性光下为
。
对 gamma 编码的 p3 颜色做 gamut 剪裁得到
在线性光下依然为
(分量值恰好为0或1时不受 gamma 影响)
对比得
。
视觉效果不如前例,但仍可接受。 色相影响略大,变化了 5.8°。
就线性光红绿蓝百分比而言, 红色高 57%,绿色低 6.7%,蓝色高 23%。
14.1.2. 最接近的颜色 (MINDE)
更好的方法是在一个感知均匀的颜色空间中通过找到最接近的在色域内的颜色进行映射 (所谓的最小 ΔE 或 MINDE)。 显然,这种技术的成功 取决于 色域映射颜色空间的均匀程度 以及所使用的 deltaE 函数的预测精度。
但是,在进行色域映射时, 色调的变化特别令人反感; 色度的变化更容易接受, 而 如果另一种选择是较大幅度的色度降低, 那么轻微的亮度变化也是可以接受的。 MINDE 对每个维度的变化赋予相同的权重, 因此产生次优的结果。
14.1.3. 色度减少
实现 MINDE 时, 颜色会在感知均匀的极坐标色彩空间中映射, 方法是保持色相不变, 并降低色度,直到颜色落入色域范围。
这可以算法化完成, 通过求解常亮、常色相射线 与色域边界的几何交点; 或采用迭代方式, 逐步减少色度直到落入色域。
注意: 为了性能考虑, 迭代通常采用二分查找。
color ( display-p3 1 1 0 ) )
要映射到 sRGB 显示器。
使用的色域映射空间为 OkLCh。
变为color ( display-p31 1 0 )
即color ( srgb1 1 -0.3463 )
color ( oklch0.96476 0.24503 110.23 )
通过逐步减少色度分量 直到结果颜色进入 sRGB 色域 (没有分量为负或大于一), 就得到了映射到色域内的颜色。
其对应color ( oklch0.96476 0.21094 110.23 )
color ( srgb0.99116 0.99733 0.00001 )
14.1.4. 过度的色度减少
此外,这种简单的 MINDE 方法对于某些颜色会得到次优结果, 主要是非常浅亮的颜色, 如黄色和青色, 当色域边界的上沿非常浅或者略呈凹面时, 常亮射线可能刚好掠过色域边界, 导致色度异常低。
选用的色彩空间会影响色域映射颜色的可接受性。
color ( display-p3 1 1 0 )
在 CIE LCH 色彩空间不断降低色度。
color ( display-p3 1 1 0 )
在 OkLCh 色彩空间不断降低色度。
14.1.5. 带局部剪裁的色度减少
可以改进简单的色度减少算法: 在每一步中, 计算当前映射颜色和该颜色的剪裁版本之间的颜色差异。 如果当前颜色超出色域边界, 但它与剪裁版本之间的颜色差异 低于刚好可见差异(JND)的阈值, 则返回该颜色的剪裁版本作为映射结果。 实际上,这在每个阶段都在进行 MINDE 映射, 但受限于色调和亮度变化非常小, 因此不明显。
color ( display-p3 1 1 0 )
在 CIE LCH 颜色空间中逐步减少色度,
并使用了局部剪裁修改。
color ( display-p3 1 1 0 )
的色度逐渐降低,但这次是在 OKLCh 色彩空间中
并使用局部裁剪修改。
14.1.6. 偏离感知均匀性:色相曲率
即使使用 deltaE2000 距离度量, 在 CIE LCH 色彩空间进行色域映射, 已知会产生次优结果, 对于色相在 270° 到 330° 范围的颜色, 会出现明显的色相漂移。
采用 OkLCh 色彩空间 和 deltaEOK 距离度量 可以避免此问题, 在所有色相角度下都有效。
14.2. CSS 色域映射到 RGB 目标
测试
颜色的实际值不会暴露给脚本,因此很难以自动化方式进行测试。可从三种算法中进行 选择也使测试变得更加困难
这三种 CSS 色域映射算法 适用于 超出以下色域的标准动态范围(SDR)CSS 颜色:
- RGB 显示器(其帧缓冲区无法表示超出色域的值)
- 具有 unorm8 后备存储的画布(因此无法存储超出色域的值)
因此,它们需要进行 CSS 色域映射。
实现可以根据质量与运行时效率之间的权衡 选择这三种算法中的任意一种, 并且只要 CSS 要求执行色域映射, 就必须使用其所选择的算法。
它们都实现了相对色度渲染意图, 因此目标色域内的颜色保持不变。
注意:其他情况, 尤其是映射到打印机色域时, 其中最大黑色级别显著高于零, 将需要使用不同的算法, 这些算法会对齐各自的黑点和白点, 从而在降低色度时, 对非常亮和非常暗的颜色 产生明度变化。
注意:这些算法适用于单独指定的 颜色, 这些颜色可以单独使用(用于页面元素),也可以组合使用(例如渐变); 对于摄影图像, 相邻像素之间的关系很重要, 并且目标是保留细节和纹理, 此时感知渲染意图更加合适, 而在这种情况下, 位于目标色域内部的颜色 也可能发生变化。
所有三种 CSS 色域映射算法的目标都是 在 OkLCh 颜色空间中 以恒定明度、恒定色相的方式降低色度。
对于在明度轴上超出范围的颜色, 如果明度大于或等于 1.0, 则返回目标颜色空间中的白色, 而如果明度小于或等于 0.0, 则返回目标颜色空间中的黑色。
测试
14.2.1. 二分查找色域映射(Local MINDE)
该二分查找算法 使用的颜色差公式为deltaEOK。 采用local-MINDE改进。 在每一步搜索中, 都计算当前映射颜色与其裁剪版本的 deltaEOK。
如果当前颜色在色域边界外, 但与裁剪版本的 deltaEOK 低于“刚刚可觉察差异”(JND)阈值, 则返回裁剪颜色作为映射结果。 这样可以获得良好的效果且几乎没有色相漂移, 避免了在凹形色域表面附近过度色度降低, 但计算量可能较大。
对 OkLCh 色彩空间, 一个 JND 代表 0.02 的 OkLCh 差值。
注: 在 CIE Lab 色彩空间, 亮度范围 0 到 100, 并用 deltaE2000 时, 一个 JND 是 2。 因为 Oklab 和 OkLCh 的亮度是 0 到 1, 用 deltaEOK 时, 一个 JND 小 100 倍。
注: 为方便实验和对比, 二分查找(Local MINDE)算法已在 Coloraide(Python)[Coloraide-MINDE] 与 color.js(JavaScript)[colorjs-MINDE]实现。
14.2.2. 二分查找色域映射(Local MINDE)伪代码示例
- 如果destination没有色域限制(XYZ-D65、XYZ-D50、Lab、LCH、Oklab、OkLCh),直接将 origin转换为destination并返回,作为色域映射后的颜色
- 令origin_OkLCh为origin从origin color space转换到OkLCh颜色空间的结果
- 如果origin_OkLCh的亮度大于等于100%, 则将`oklab(1 0 0 / origin.alpha)`转换为destination,并返回作为色域映射后的颜色
- 如果origin_OkLCh的亮度小于等于0%, 则将`oklab(0 0 0 / origin.alpha)`转换为destination,并返回作为色域映射后的颜色
- 令 inGamut(color) 为一个函数,判断传入颜色是否在 destination色域内。 对于HSL和HWB,如果该颜色在sRGB色域内则返回true。
- 如果 inGamut(origin_OkLCh) 为true,直接将origin_OkLCh 转换为destination并返回,作为色域映射后的颜色
- 否则,令 delta(one, two) 为一个函数,计算两颜色的deltaEOK差异
- 令 JND 为0.02
- 令 epsilon 为0.0001
- 令 clip(color) 为一个函数,将color转换为destination, 将每个分量钳制到其参考范围内,并返回结果
- 令 current 为 origin_OkLCh
- 令 clipped 为 clip(current)
- 令 E 为 delta(clipped, current)
-
如果 E < JND
- 返回 clipped 作为色域映射后的颜色
- 令 min 为零
- 令 max 为 origin_OkLCh 的OkLCh色度
- 令 min_inGamut 为布尔值,表示min当前是否在色域内,初始设为true
-
当 (max - min 大于 epsilon) 时,重复以下步骤
- 令 chroma = (min + max) /2
- 将current的色度分量设为chroma
- 如果 min_inGamut 为 true 且 inGamut(current) 也为 true,则 把min设为chroma,继续重复这些步骤
-
否则,执行以下步骤:
- 令 clipped 为 clip(current)
- 令 E 为 delta(clipped, current)
-
如果 E < JND
- 如果 (JND - E < epsilon),则返回 clipped 作为色域映射后的颜色
-
否则,
- 将min_inGamut设为false
- 将min设为chroma
- 否则,将max设为chroma,继续重复这些步骤
- 返回 clipped 作为色域映射后的颜色
14.2.3. EdgeSeeker色域映射
EdgeSeeker算法是一种基于几何与查找表的色域映射方法, 最初由Alexey Ardov为color.js库开发 [colorjs-EdgeSeeker]。
对于任意给定的色相,色域边界切片 由上方的曲线段和下方的线性段组成, 在该色相的最大色度点处连接。
初始化本算法时, 针对目标RGB空间, 构建一个查找表(LUT), 包含每个色相切片上的最大色度OkLCh颜色。 对实际映射颜色的色相,使用查找表中最近值做线性插值, 估算其对应的最大色度值。
随后计算常亮射线与色域边界的交点, 对于边界下部(线性段)非常快, 上部(曲线段)效率也很高。
这种方法效果理想,但查找表(LUT)会占用更多内存。
14.2.4. EdgeSeeker 色域映射的示例伪代码
14.2.5. Ray Trace 色域映射
Ray Trace 算法是一种几何方法, 用于 RGB 色域映射, 可在保持明度不变的情况下快速降低色度。 它最初由 Isaac Muse 为 Coloraide Python 库开发 [Coloraide-Ray-Trace].
待映射的颜色首先被转换为 Oklch, 然后生成该颜色的无彩色版本, 它将成为中性轴锚点。 接着这两种颜色都会被转换为 目标 RGB 空间的线性光版本。
由于此时色域边界是一个轴对齐立方体, 求交点会更快。
从 RGB 立方体内部发射一条射线, 起点为锚点,终点为当前颜色。 然后找到这条路径 与 RGB 色域表面的交点; 这就是色域映射颜色的第一近似值。
由于 RGB 空间并非感知均匀, 恒定色相、恒定明度的射线 在 RGB 空间中实际上是一条曲线路径。
第一近似值会被转换回 Oklch, 以便在感知颜色空间中修正颜色, 方法是将该点重新投影回色度降低路径, 从而修正颜色的色相和明度。 修正后的颜色成为新的当前颜色, 并且应当是色度降低线上更接近的颜色。
此过程会重复进行(最多再进行三次), 每次都会在路径上找到一个更好、更接近的颜色。 最后,使用简单裁剪来处理浮点运算误差。
其结果与使用较低 JND 的局部 MINDE 进行二分查找相当, 但速度更快, 而且时间更可预测、更一致。
图片版权 Isaac Muse。
注:为了实验和 比较不同实现, Ray Trace 的实现可在 Coloraide 库(Python)中找到 [Coloraide-Ray-Trace] ,以及 color.js 库(JavaScript)中找到 [colorjs-RayTrace].
14.2.6. Ray Trace 色域映射的示例伪代码
- 如果 destination 没有色域限制(XYZ-D65、XYZ-D50、Lab、LCH、Oklab、OkLCh),则将 origin 转换到 destination,并将其作为色域映射后的颜色返回
- 令 origin_OkLCh 为从 origin color space 转换到 OkLCh 颜色空间的 origin
- 如果 origin_OkLCh 的明度大于或等于 100%, 则将 `oklab(1 0 0 / origin.alpha)` 转换到 destination,并将其作为色域映射后的 颜色返回
- 如果 origin_OkLCh 的明度小于或等于 0%, 则将 `oklab(0 0 0 / origin.alpha)` 转换到 destination,并将其作为色域映射后的 颜色返回
- 令 l_origin 为 origin_OkLCh 的 OkLCh 明度分量
- 令 h_origin 为 origin_OkLCh 的 OkLCh 色相分量
- 令 anchor 为一个无彩色 OkLCh 颜色,该颜色以 l_origin 作为明度、以 0 作为色度并以 h_origin 作为色相, 然后转换到 destination 的线性光形式
- 令 origin_rgb 为转换到 destination 的线性光形式的 origin_OkLCh
-
如果 origin_rgb 不在色域内
- 令 low 为 0.0 + 1E-12 1
- 令 high 为 1.0 - 1E-12 2
- 令 last 为 origin_rgb
-
对于 (i=0; i<4; i++)
-
如果 (i > 0)
- 令 current_OkLCh 为转换到 OkLCh 的 origin_rgb
- 令 current_OkLCh 的明度为 l_origin
- 令 current_OkLCh 的色相为 h_origin 3
- 令 origin_rgb 为转换到 destination 的线性光形式的 current_OkLCh
- 从 start = anchor 到 end = origin_rgb 投射一条射线, 并令 intersection 为 该射线 与色域边界的交点
- 如果未找到交点, 则令 origin_rgb 为 last 并退出循环 5
- 如果 (i >0) 且(origin_rgb 的每个分量都介于 low 和 high 之间),则 令 anchor 为 origin_rgb 4
- 令 origin_rgb 为 intersection
- 令 last 为 intersection
-
如果 (i > 0)
- 令 clip(color) 为一个函数,该函数将 color 转换到 destination, 将每个分量限制在该分量参考范围的边界内, 并返回结果
- 将 clipped 设置为 clip(origin_rgb)
- 将 clipped 作为色域映射后的颜色返回
- 令 bmin 和 bmax 分别为 包含色域下边界和上边界的三元素数组 6
- 令 tfar 为无穷大(或某个非常大的数)
- 令 tnear 为负无穷大(或某个绝对值非常大的负数)
- 令 direction 为一个三元素数组
-
对于 (i = 0; i < 3; i++):
- 令 a 为 start [i]
- 令 b 为 end [i]
- 令 d 为 b - a
- 令 direction [i] 为 d
-
如果 abs(d) > 1E-12 7
- 令 inv_d 为 1 / d
- 令 t1 为 (bmin [i] - a ) * inv_d
- 令 t2 为 (bmax [i] - a ) * inv_d
- 令 tnear 为 max(min(t1, t2), tnear )
- 令 tfar 为 min(max(t1, t2), tfar )
-
否则,如果(a < bmin[i] 或 a >
bmax[i])
- 返回“未找到交点”
-
如果(tnear > tfar 或 tfar < 0)
- 返回“未找到交点”
-
如果 tnear < 0
- 令 tnear 为 tfar 8
-
如果 tnear 为无穷大(或与初始的非常大数值匹配)
- 返回“未找到交点”
-
对于 (i =0; i < 3; i++):
- 令 result [i] 为 start [i] + direction [i] * tnear
- 返回 result
14.2.6.1. Ray Trace 算法的脚注
- 假定最小值为 0, 并且所有通道具有相同的最小值。 该值相对于单位类型应足够小。 指定值 1e-12 适用于 64 位,但对于 32 位,应使用 1e-6。
- 1.0 表示色域内通道的最大值, 并且假定所有通道具有相同的最大值。
- 如果当前颜色已经偏离, 这会将其放回色度缩减曲线上。
- 这意味着 origin_rgb 位于色域表面之下, 因此我们将其用作更靠近色域表面的锚点。
- 这是为灾难性故障提供的处理方式, 在这种情况下,某个特定的感知映射空间 会由于宽得离谱的颜色(位于可见光谱之外) 而完全失效。 预计 CSS 中的非虚构颜色绝不应触发这种情况。
- 对于典型的 RGB 空间, 如果每个分量的色域边界均为 0 和 1, 则可以将其简化为单个常量, 而不是三元素数组。
- 该值相对于单位类型应足够小。 指定值 1e-12 适用于 64 位,但对于 32 位,应使用 1e-6。
- 优先采用沿 start -> end 方向遇到的第一个交点。
15. 解析 <color> 值
除非为特定属性另有规定,指定的颜色将解析为 计算颜色,然后进一步解析为 使用颜色,如下所述。
测试
15.1. 解析 sRGB 值
这适用于:
它不适用于:
-
使用 srgb 或 srgb-linear 颜色空间的 color() 值。
如果 sRGB 颜色由作者显式指定为命名颜色, 则声明值是该命名颜色,并转换为 ASCII 小写。 计算值和使用值 是对应的 sRGB 颜色, 与指定的 alpha 分量配对 (将其限制到 [0, 1] 之后), 如果未指定,则默认为不透明)。
否则,声明值、计算值和使用值 是对应的 sRGB 颜色, 结合指定的 alpha 分量 (在裁剪到 [0, 1] 之后) ,如果未指定则默认为不透明)。
由于历史原因,当 calc() 在 sRGB 颜色中 化简为单个值时, 声明值序列化时不会带有 "calc(" ")" 包裹。
同样由于历史原因, 当 calc() 化简为单个值时, 颜色值会被裁剪到 [0.0, 255.0]。
这种裁剪也会处理诸如 Infinity、-Infinity 和 NaN 之类的值, 它们会分别被裁剪为 255、0 和 0。
测试
15.2. 解析 Lab 和 LCH 值
声明值、计算值和使用值 是相应的 CIE Lab 或 LCH 颜色 (在限制 L、C 和 H 后) 与指定的 alpha 分量配对 (作为 <number>,而非 <percentage>; 如果未指定则默认为不透明)。
虽然 a、b 和 C 的数值 理论上没有上限, 但对于趋于无穷大的数值, 实现上可能会有 实现自定义的极限。
15.3. 解析 Oklab 和 OkLCh 数值
声明值、计算值和使用值 为对应的 Oklab 或 OkLCh 颜色 (L、C、H 分量已经裁剪后) 以及指定的 alpha 分量 (为<number> ——不是<percentage>; 未指定时 alpha 默认为不透明)。
虽然 a、b 和 C 的取值理论上无限制, 但对于趋近无穷大的数值, 实现上可能有实现自定义的极限。
15.4. 解析 color() 函数的值
声明值、计算值和使用值 是指定色彩空间中的颜色, 与指定的 alpha 分量配对 (作为 <number>,而非 <percentage>; 如果未指定则默认为不透明)。
对于在 xyz 颜色空间 中指定的颜色, 这是 xyz-d65 颜色空间 的别名, 计算值和使用值 采用 xyz-d65 颜色空间。
虽然 r、g、b、x、y 和 z 的数值 理论上没有上限, 但对于趋近无穷大的数值, 实现上可能会有 实现自定义的极限。
15.5. 解析其他颜色
这适用于
如果颜色由作者显式指定为 系统颜色 或已弃用的系统颜色, 则声明值为其自身,并转换为 ASCII 小写形式。
< mark style = "color: MarkText; background: Mark" > example</ mark >
background-color 的声明值为“mark”。
对于系统颜色,其 计算值和使用值 是其颜色空间中的相应颜色, 并与指定的 alpha 分量配对 (将其限制在 [0, 1] 范围内之后)。
对于已弃用的系统颜色, 其计算值和使用值 是相应系统颜色的颜色 (如附录 A:已弃用的 CSS 系统颜色中所列) 在其颜色空间中的值, 并与指定的 alpha 分量配对 (将其限制在 [0, 1] 范围内之后)。
.foo{ color : InfoText; }
与 InfoText 对应的系统颜色是 CanvasText。 假设 CanvasText 为 #333; 那么 InfoText 的计算值 也将为 #333;
< button style = "color: ButtonText; background: ButtonFace" ></ button >
color 属性的声明值为“buttontext”, 而计算值例如可以是 rgb(0, 0, 0)。
但是,系统颜色和已弃用的系统颜色 不得被 强制颜色模式更改。
transparent 的声明值为“transparent”, 而计算值和使用值为透明黑色。
currentcolor 关键字的计算值为其自身。
在 color 属性中, currentcolor 的使用值是解析后的 继承值。 在任何其他属性中, 其使用值是同一元素上 color 属性的使用值。
注意:这意味着,如果 currentcolor 值被继承, 它将作为关键字被继承, 而不是作为 color 属性的值被继承, 因此后代将使用它们自己的 color 属性 来解析该值。
< div > < p > Assume this example text is long enough to wrap on multiple lines.</ p > </ div >
以及以下 CSS:
div{ color : forestgreen; text-shadow : currentColor; } p{ color : mediumseagreen; } p::firstline{ color : yellowgreen; }
继承属性 text-shadow 在 第一行片段上的使用值将为 yellowgreen。
测试
16. 序列化<color> 值
本节更新并替换了 CSS 对象模型中第 序列化 CSS 值 的那一部分, 该部分涉及 <color> 值的序列化。
在本节中,规范中使用的字符串及其对应字符如下。
| 字符串 | 字符 |
|---|---|
| " " | U+0020 SPACE |
| "#" | U+0023 NUMBER SIGN |
| "," | U+002C COMMA |
| "-" | U+002D HYPHEN-MINUS |
| "." | U+002E FULL STOP |
| "/" | U+002F SOLIDUS |
| "none" | U+006E LATIN SMALL LETTER N U+006F LATIN SMALL LETTER O U+006E LATIN SMALL LETTER N U+0065 LATIN SMALL LETTER E |
字符串 "." 应作为小数分隔符使用, 不论区域设置如何, 且不应使用千位分隔符。
对于支持 缺失颜色分量 的语法形式, 值 none(等价于 NONE、nOnE 等) 应以全小写 作为字符串 "none" 进行序列化。
16.1. 序列化 alpha 值
这适用于任何可以采用可选 alpha 值的 <color> 值。 它不适用于 opacity 属性。
如果将 alpha 限制在 [0, 1] 范围内之后其值为 1, 则序列化时省略该值; 默认采用隐式值 1(完全不透明)。
如果 alpha 是 1 以外的任何其他值, 则按照下文所述,将其显式包含在序列化结果中。
由于历史原因, alpha 的序列化方式 取决于颜色是使用 传统颜色 语法 还是现代颜色 语法指定的。
16.1.1. 序列化传统 alpha 值
如果该值在内部表示为 0 到 255(含)之间的整数(即 8 位无符号整数), 则执行以下步骤:
- 令 alpha 为给定的整数。
- 如果存在一个 0 到 100(含)之间的整数, 该整数乘以 2.55 并舍入到最接近的整数后 (如果两个值同样接近,则向上舍入)等于 alpha, 则令 rounded 为该整数除以 100。
- 否则,令 rounded 为 alpha 除以 0.255 并舍入到最接近的整数 (如果两个值同样接近,则向上舍入), 再除以 1000。
- 返回将 rounded 作为 <number> 进行序列化的结果。
否则,返回对给定值进行序列化的结果 (作为 <number>,而不是 <percentage>), 其方式与现代 alpha 序列化相同。
例如, 如果 alpha 存储为 8 位无符号整数 237, 则整数 93 满足条件, 因为 Math.round(93 * 2.55) 为 237, 因此 alpha 被序列化为“0.93”。
但是, 如果 alpha 存储为 8 位无符号整数 236, 则不存在这样的整数 (92 映射为 235,而 94 映射为 240), 因此,由于 236 ÷ 0.255 = 925.490196078, alpha 被序列化为“0.92549” (不超过 6 位数字,并省略尾随零)。
对于传统颜色 语法, 保留 alpha 值时所采用的精度, 以及由此决定的序列化值的小数位数, 必须至少足以 往返转换整数百分比值。 因此,序列化值必须包含 至少两位小数 (除非尾随零已被移除)。 值必须 向 +∞ 舍入,而不能截断。
例如,alpha 值 12.3456789% 可以序列化为字符串 “0.12”、“0.123”、“0.1234”或“0.12346” (由于后续数字为 6, 因此将数字 5 向 +∞ 舍入), 或者采用任何更长的、以相同形式舍入的序列化结果。
16.1.2. 序列化现代 alpha 值
返回对给定值进行序列化的结果 (作为 <number>,而不是 <percentage>)。
<number> 值使用十进制表示, 并以“.”字符作为小数分隔符。 不得省略前导零。 必须省略尾随零。
例如,alpha 值 70% 将被序列化为字符串 “0.7”, 其中小数分隔符之前有前导零, 以“.”作为小数分隔符 (即使当前区域设置使用其他字符, 例如“,”), 并且“7”之后的所有数字都将为“0”,因此被省略。
对于现代颜色 语法, 保留 <alpha-value> 时所采用的精度, 以及由此决定的序列化值的小数位数, 必须足以 往返转换 16 位十进制值。
序列化值必须包含 六位小数 (除非尾随零已被移除)。
值必须 向 +∞ 舍入,而不能截断。
例如, 给定颜色 color(srgb 1 0 0 / 0.5), alpha 值应被序列化 为字符串“0.5”。 如果仅以 10 位精度存储, 则内部值将为 512/1023, 即 0.50048875855, 或按六位有效数字表示为“0.500489”, 接近所需值但并不精确。 使用 12 位时,该值将为 2048/4095, 即 0.50012210012, 按六位有效数字表示为 0.500122, 仍然不精确。
将其存储为 128/255 也是不正确的, 因为该值为 0.50196078431, 或按六位有效数字表示为 0.501960。
为避免累积舍入误差, 建议在内部为每个 alpha 分量使用 16 位、半精度浮点数或浮点数存储。
由于在有效范围之外指定的 <alpha-value> 会在解析时被限制,因此声明值也将被限制。 但是,根据 CSS 值 4第 10.12 节 范围检查,使用 calc() 指定的 <alpha-value> 在序列化指定形式时不会被限制; 但计算值会被限制。
16.2. 序列化 sRGB 值
以下 sRGB 值的序列化形式:
派生自声明值。
当序列化由作者设置为 CSS 命名颜色、 系统颜色、 弃用颜色, 或 transparent 的属性值时,因此,对于声明值, 会保留采用 ASCII 小写形式的 关键字值。 对于计算值和使用值, 则使用对应的 sRGB 值。
因此,transparent 的序列化声明值是字符串 "transparent", 而 transparent 的序列化计算值是字符串 "rgba(0, 0, 0, 0)"。
对于所有其他 sRGB 值, 声明值、计算值和使用值 都是对应的 sRGB 值。
在序列化期间, 任何缺失值 都会转换为 0, 如果所选的序列化形式 (例如使用逗号分隔符的旧式 颜色语法, 或 sRGB 值的 HTML 兼容序列化) 无法表示 none 关键字。 当至少有一个分量缺失 且该值可以序列化为支持 none 的形式时, 会按照 § 16.2.2 sRGB 值的 CSS 序列化 中所述选择该形式,从而将缺失颜色分量保留为 none。
16.2.1. sRGB 值的 HTML 兼容序列化
如果以下条件都为真:
- 颜色空间是 sRGB
- alpha 为 1
- RGB 分量值在内部表示为 0 到 255(含)之间的整数(即 8 位 无符号整数)
- 请求 HTML 兼容序列化
那么对应的 sRGB 值将按 6 位 十六进制颜色记法序列化,如下:
一个由七个字符组成的字符串:以字符 "#" 开头,后面紧跟红色分量、绿色分量和蓝色分量的两位十六进制表示,按此顺序,使用 ASCII 小写十六进制数字。不允许有空格。
context. fillStyle= "rgb(255, 0, 255)" console. log( context. fillStyle); // "#ff00ff"
颜色空间是 sRGB,表示方式为每个分量 8 位, 数据格式不会产生 none 值,也不支持扩展范围值, 且 alpha 为 1。
HTML 兼容序列化的字符串是 "#ff00ff"(不是 "#FF00FF")。
否则,对于 sRGB 使用 sRGB 值的 CSS 序列化,而 对于其他颜色空间,则使用 序列化该 <color> 值时相应的序列化方式。
context. fillStyle= "lab(29% 39 20)" console. log( context. fillStyle); // "lab(29 39 20)"
CSS 序列化的字符串是 "lab(29 39 20)"。
context. fillStyle= "#ff00ffed" console. log( context. fillStyle); // "rgba(255, 0, 255, 0.93)"
alpha 不为 1,所以 CSS 序列化的字符串是 "rgba(255, 0, 255, 0.93)"。
16.2.2. sRGB 值的 CSS 序列化
如果该值没有缺失颜色分量, 则对应的 sRGB 值使用 rgb() 或 rgba() 形式 (取决于(限制后的)alpha 是否恰好为 1), 函数名称中的所有字母均使用 ASCII 小写 形式。
为实现兼容性,sRGB 分量值 会以 <number> 形式序列化,而不是 <percentage>。 同样出于兼容性考虑, 分量值会以十进制序列化, 范围为 [0-255],而不考虑 存储这些值时所使用的位深度。
如前所述, 值为 1 的 alpha 值不会显式序列化。 同样出于兼容性考虑,如果 alpha 恰好为 1, 则使用 rgb() 形式, 并隐式表示 alpha; 否则,使用 rgba() 形式, 并包含显式的 alpha 值。
为实现兼容性, 使用带逗号分隔符的旧式形式; 每个逗号后恰好跟一个 ASCII 空格。 这也包括用于 将 rgba() 的蓝色分量 与 alpha 值分隔的逗号(而不是斜杠)。
但是,带逗号分隔符的旧式 颜色语法 无法表示 none。 如果该值至少有一个缺失颜色分量, 则会选择相应的序列化形式 以将这些分量保留为 none 关键字, 具体取决于声明 值所使用的颜色函数:
-
对于 rgb() 和 rgba() 值 (这是此列表中唯一语法接受 none 的 sRGB 形式; 十六进制颜色、命名颜色、系统 颜色、 弃用颜色, 以及 transparent 都没有参数化语法, 因此绝不会具有缺失颜色分量), 该值会序列化为 color() 函数, 使用 srgb 颜色空间, 而不是序列化为现代的空格分隔 rgb() 形式, 即使该形式也接受 none: "color(srgb" 后跟一个空格, 然后是三个非 alpha 分量以空格分隔的列表, 这些分量会在 [0, 1] 参考范围内序列化为 <number> (如果缺失,则序列化为 none), 随后(仅当 alpha 不为 1 或缺失时) 跟上 " / " 和 alpha 分量 (按照 alpha 规则进行序列化, 如果缺失,则序列化为 none), 最后跟上 ")"。
-
对于 hsl() 和 hsla() 值, 该值使用 现代的(空格分隔)hsl() 语法进行序列化, 当存在 alpha 分量时,其前面使用斜杠。 无论该值在编写时 是否使用 hsla() 别名, 函数名称都是 "hsl"(采用 ASCII 小写)。 色相会以度为单位序列化为规范化的 <number>, 饱和度和亮度序列化为 <percentage>, alpha (仅当不为 1 或缺失时包含) 按照alpha 规则进行序列化; 任何缺失分量都会序列化为 none 关键字。
-
对于 hwb() 值, 该值使用 现代的(空格分隔)hwb() 语法进行序列化, 当存在 alpha 分量时,其前面使用斜杠。 函数名称是 "hwb", 采用 ASCII 小写。 色相会以度为单位序列化为规范化的 <number>, 白度和黑度序列化为 <percentage>, alpha (仅当不为 1 或缺失时包含) 按照alpha 规则进行序列化; 任何缺失分量都会序列化为 none 关键字。
注: 这意味着,包含 none 的 hsl() 或 hwb() 值 在经过序列化往返后仍使用其自身的颜色函数, 而不会退化为 rgba()(其旧式形式无法表示 none), 而包含 none 的 rgb() 值 则通过 color(srgb …) 进行序列化。 现代的空格分隔 rgb() 形式本身 也可以表示 none, 但 sRGB CSS 序列化改为使用 color(srgb …), 以便与所有其他颜色空间在非旧式形式下的序列化方式 保持一致。 这与 CSS Color 5 § 11.2 序列化源颜色中定义的相对颜色语法行为一致。
hwb ( 740 deg 20 % 30 % /50 % )
首先会被规范化为
hwb ( 20 20 % 30 % /50 % )
然后转换为 sRGB 并序列化为
rgba ( 178.5 , 93.5 , 51 , 0.5 )
返回结果的精度 将在下文说明。
hwb ( 20 none30 % / none)
包含缺失颜色分量 (白度和 alpha 都是 none), 因此它不会通过 rgba() 进行序列化。 相反,它会使用现代的 hwb() 语法序列化为
hwb ( 20 none30 % / none)
并保留每个 none 值。
注: 与 CSS Color 3 不同, rgb() 函数的参数 类型为 <number>,而不是 <integer>。 因此,任何高于八位的精度 都通过小数部分表示。
sRGB 分量值所保留的精度, 以及由此决定的序列化值中的有效数字位数, 本规范未作定义, 但至少必须足以 对八位值进行无损往返转换。 值必须向 +∞ 舍入,而不是 截断。
注: 对于期望 从 getComputedStyle 返回的颜色值具有 <integer> 分量值的脚本作者, 建议更新这些脚本,使其也能处理 <number>。
例如,
rgb ( 146.064 107.457 131.223 )
现在是有效的,并且等于
rgb ( 57.28 % 42.14 % 51.46 % )
两者的一种符合规范的序列化形式, 是字符串 "rgb(146.06, 107.46, 131.2)"。
任何分量值末尾的小数零都必须省略; 如果小数部分全部由零组成, 则小数点也必须省略。 这意味着,以整数分量值指定的 sRGB 颜色 将序列化为向后兼容的整数值。
16.3. 序列化 Lab 和 LCH 值
lch() 和 lab() 值的序列化形式 派生自计算值,并使用 lab() 或 lch() 形式, 函数名称使用ASCII 小写字母。
分量值以十进制序列化; L、a、b 和 C 分量值 序列化为 <number>, 酌情使用 Lab 百分比参考范围或 LCH 百分比参考范围 执行百分比到数字的转换; 因此 0% L 映射到 0, 100% L 映射到 100。 必须使用单个 ASCII 空格字符 " " 作为分量值之间的分隔符。
任何组件值中的尾随小数部分零必须省略;如果小数部分全为零,小数点也必须省略。
保留 lab() 组件值的精度,即序列化值中的有效数字位数,本规范未定义,但由于广色域,必须足够以进行 0 到 100 之间的 L 值以及 ±127 之间的 a 和 b 值的往返操作,至少需要 16 位精度;这将导致至少有三位小数,除非尾随零已被省略。(建议内部存储为半浮点数或浮点数)。值必须 向 +∞ 取整,而非截断。
注意: 超宽色域空间中的 a 和 b 值可以超出 ±125。例如,所有的 prophoto-rgb 主色和次色都超过了此范围,但在 ±200 之内。
如前所述,单位 alpha 值不会显式序列化。非单位 alpha 值必须显式序列化,并且字符串 " / "(一个 ASCII 空格,然后斜杠,然后另一个空格)必须用于将 b 组件值与 alpha 值分隔开。
以下值的序列化形式为:
lch ( 56.2 % 83.6 357.4 /93 % )
字符串 "lch(56.2 83.6 357.4 / 0.93)",而不是 "lch(56.2% 83.6 357.4 / 0.93)"
16.4. 序列化 Oklab 和 OKLCh 值
oklch() 和 oklab() 值的序列化形式 派生自计算值,并使用 oklab() 或 oklch() 形式, 函数名称使用ASCII 小写字母。
分量值以十进制序列化; L、a、b 和 C 分量值 序列化为 <number>, 酌情使用 Oklab 百分比参考范围或 OKLCh 百分比参考范围 执行百分比到数字的转换; 因此 0% L 映射到 0, 100% L 映射到 1.0。 必须使用单个 ASCII 空格字符 " " 作为分量值之间的分隔符。
以下值的序列化形式为:
oklab ( 54.0 % -0.10 -0.02 )
字符串 "oklab(0.54 -0.1 -0.02)",而不是 "oklab(54 -0.1 -0.02)" 或 "oklab(54% -0.1 -0.02)"
任何组件值中的尾随小数部分零必须省略;如果小数部分全为零,小数点也必须省略。
以下值的序列化形式为:
oklch ( 56.43 % 0.0900 123.40 )
字符串 "oklch(0.5643 0.09 123.4)",而不是 "oklch(0.5643 0.0900 123.40)"。
保留 oklab() 组件值的精度,即序列化值中的有效数字位数,本规范未定义,但由于广色域,必须足够以进行 0 到 1(0% 到 100%)之间的 L 值,以及 ±0.5 之间的 a、b 和 C 值的往返操作,至少需要 16 位精度;这将导致至少有五位小数,除非尾随零已被省略。(建议内部存储为半浮点数或浮点数)。值必须 向 +∞ 取整,而非截断。
注意: 超宽色域空间中的 a、b 和 C 值可以超出 ±0.5。例如,prophoto-rgb 的绿色和蓝色主色的 C 值分别为 0.526 和 1.413,超出了该范围。
如前所述,单位 alpha 值不会显式序列化。非单位 alpha 值必须显式序列化,并且字符串 " / "(一个 ASCII 空格,然后斜杠,然后另一个空格)必须用于将最终的颜色组件值(b 或 C)与 alpha 值分隔开。
16.5. 序列化 color() 函数的值
color() 值的序列化形式 派生自计算值 并使用 color() 形式, 函数名称 和颜色空间名称的字母使用 ASCII 小写。
分量值以十进制序列化, 作为 <number>。 必须使用单个 ASCII 空格字符 " " 作为分隔符, 用于分隔各个分量值, 以及颜色空间名称与第一个颜色分量。
以下值的序列化值
color ( dIsPlAy-P30.964 0.763 0.787 )
是字符串 "color(display-p3 0.96 0.76 0.79)", 如果保留两位小数。 请注意,0.787 已向上舍入为 0.79, 而不是截断为 0.78。
任何分量值末尾的小数零都必须省略; 如果小数部分全部由零组成, 则小数点也必须省略。
以下值的序列化值
color ( rec20200.400 0.660 0.340 )
是字符串 "color(rec2020 0.4 0.66 0.34)", 而不是 "color(rec2020 0.400 0.660 0.340)"。
如果颜色空间是 sRGB,则序列化结果中仍然明确要求包含该颜色空间。
对于预定义颜色空间, 往返转换所需的最低精度如下:
| 颜色空间 | 最小位数 |
|---|---|
| srgb | 10 |
| srgb-linear | 12 |
| display-p3 | 10 |
| display-p3-linear | 12 |
| a98-rgb | 10 |
| prophoto-rgb | 12 |
| rec2020 | 12 |
| xyz, xyz-d50, xyz-d65 | 16 |
(建议内部存储使用每个分量 16 位、半精度浮点数或浮点数)。 值必须向 +∞ 舍入,而不是 截断。
注: 与 rgb()、hsl() 等旧式形式相比, color(srgb) 具有更高的最低精度要求。 因此,偏好更高精度的样式表作者 建议使用 color(srgb) 形式。
如前所述, 值为 1 的 alpha 值不会显式序列化。 非 1 的 alpha 值必须显式序列化, 并且必须使用字符串 " / " (一个 ASCII 空格,然后是正斜杠,再然后是另一个空格) 来分隔 最后一个颜色分量值 与 alpha 值。
以下值的序列化值
color ( prophoto-rgb0.2804 0.40283 0.42259 /85 % )
是字符串 "color(prophoto-rgb 0.28 0.403 0.423 / 0.85)", 如果保留三位小数。
16.6. 序列化其他颜色
这适用于 currentcolor。
该值的序列化形式来源于 计算值,并且颜色名称中的字母使用 ASCII 小写。
currentColor 的序列化形式为字符串 "currentcolor"。
17. 序列化 <opacity-value>
本规则适用于 opacity 属性。
如果指定的不透明度值 与字面 <percentage-token> 匹配(即未使用 calc()), 则应将其序列化为等价的 <number>(0% 对应 0,100% 对应 1)值。 否则,不透明度的指定值 应按语法的标准序列化方式序列化。
这种 <number> 值用十进制表示, 小数点用 “.” 。 不可省略前导零。 必须省略末尾零。
超出 [0,1] 区间的不透明度值 保留原样,不进行截断,在序列化的指定值中体现。
不透明度值的保留精度, 以及序列化时的小数位数, 本规范未明确定义, 但至少要足以 完整还原为整数百分比值。 因此,序列化值必须至少包含 两位小数(除非末尾零已去除)。 数值必须 向 +∞ 舍入,不能直接截断。
18. 默认样式规则
以下样式表是说明性的,而非规范性的。此样式表可作为实现 HTML 文档默认样式的一部分。
/* 桌面用户代理的传统超链接颜色 */ :link{ color : LinkText; } :visited{ color : VisitedText; } :active{ color : ActiveText; }
19. 颜色转换的示例代码
本节不是规范性的。
测试
本节不是规范性的,因此不需要测试。
为了更清晰地表达,使用了一个库进行矩阵乘法。(这比内联所有的乘法和加法更易读)。矩阵采用列主序排列。
// 颜色转换示例代码 // 转换也可以使用 ICC 配置文件和色彩管理系统完成 // 为简明起见,矩阵乘法调用了库(multiply-matrices.js) // 标准白点,用四位 CIE x,y 色度定义 const D50= [ 0.3457 / 0.3585 , 1.00000 , ( 1.0 - 0.3457 - 0.3585 ) / 0.3585 ]; const D65= [ 0.3127 / 0.3290 , 1.00000 , ( 1.0 - 0.3127 - 0.3290 ) / 0.3290 ]; // sRGB 相关函数 function lin_sRGB( RGB) { // 将 sRGB 数值数组转换为线性光(未压缩)形式 // 色域内的取值范围为 [0 - 1] // https://en.wikipedia.org/wiki/SRGB // 拓展传递函数: // 对负值,线性部分沿轴对称延拓, // 然后使用对称的幂函数。 return RGB. map( function ( val) { let sign= val< 0 ? - 1 : 1 ; let abs= Math. abs( val); if ( abs<= 0.04045 ) { return val/ 12.92 ; } return sign* ( Math. pow(( abs+ 0.055 ) / 1.055 , 2.4 )); }); } function gam_sRGB( RGB) { // 将线性光 sRGB 数组(范围 0.0-1.0)转换为伽马校正形式 // https://en.wikipedia.org/wiki/SRGB // 拓展传递函数: // 对负值,线性部分沿轴对称延拓, // 然后在该阈值以下使用对称幂函数。 return RGB. map( function ( val) { let sign= val< 0 ? - 1 : 1 ; let abs= Math. abs( val); if ( abs> 0.0031308 ) { return sign* ( 1.055 * Math. pow( abs, 1 / 2.4 ) - 0.055 ); } return 12.92 * val; }); } function lin_sRGB_to_XYZ( rgb) { // 将线性光 sRGB 数组转换为 CIE XYZ // 使用 sRGB 自身的白点 D65(不做色度适应) var M= [ [ 506752 / 1228815 , 87881 / 245763 , 12673 / 70218 ], [ 87098 / 409605 , 175762 / 245763 , 12673 / 175545 ], [ 7918 / 409605 , 87881 / 737289 , 1001167 / 1053270 ], ]; >return multiplyMatrices( M, rgb); } function XYZ_to_lin_sRGB( XYZ) { // 将 XYZ 转换为线性光 sRGB var M= [ [ 12831 / 3959 , - 329 / 214 , - 1974 / 3959 ], [ - 851781 / 878810 , 1648619 / 878810 , 36519 / 878810 ], [ 705 / 12673 , - 2585 / 12673 , 705 / 667 ], ]; return multiplyMatrices( M, XYZ); } // display-p3 相关函数 function lin_P3( RGB) { // 将 display-p3 RGB 数组(范围 0.0 - 1.0)转换为线性光形式 return lin_sRGB( RGB); // 与 sRGB 相同 } function gam_P3( RGB) { // 将线性光 display-p3 RGB 数组(范围 0.0-1.0)转换为伽马校正形式 return gam_sRGB( RGB); // 与 sRGB 相同 } function lin_P3_to_XYZ( rgb) { // 将线性光 display-p3 值转换为 CIE XYZ // 使用 D65(不做色度适应) // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html var M= [ [ 608311 / 1250200 , 189793 / 714400 , 198249 / 1000160 ], [ 35783 / 156275 , 247089 / 357200 , 198249 / 2500400 ], [ 0 / 1 , 32229 / 714400 , 5220557 / 5000800 ], ]; return multiplyMatrices( M, rgb); } function XYZ_to_lin_P3( XYZ) { // 将 XYZ 转换为线性光 P3 var M= [ [ 446124 / 178915 , - 333277 / 357830 , - 72051 / 178915 ], [ - 14852 / 17905 , 63121 / 35810 , 423 / 17905 ], [ 11844 / 330415 , - 50337 / 660830 , 316169 / 330415 ], ]; return multiplyMatrices( M, XYZ); } // prophoto-rgb 函数 function lin_ProPhoto( RGB) { // 将 prophoto-rgb 数组转换为线性光(未压缩)形式 // 色域内颜色的取值范围为 [0.0 - 1.0] // 传递曲线为 gamma 1.8,包含小的线性段 // 拓展传递函数 const Et2= 16 / 512 ; return RGB. map( function ( val) { let sign= val< 0 ? - 1 : 1 ; let abs= Math. abs( val); if ( abs<= Et2) { return val/ 16 ; } return sign* Math. pow( abs, 1.8 ); }); } function gam_ProPhoto( RGB) { // 将线性光 prophoto-rgb 数组(范围 0.0-1.0)转换为伽马校正形式 // 传递曲线为 gamma 1.8,包含小的线性段 // TODO: 对负值,沿轴反射延拓线性段,然后加上幂函数 const Et= 1 / 512 ; return RGB. map( function ( val) { let sign= val< 0 ? - 1 : 1 ; let abs= Math. abs( val); if ( abs>= Et) { return sign* Math. pow( abs, 1 / 1.8 ); } return 16 * val; }); } function lin_ProPhoto_to_XYZ( rgb) { // 将线性光 prophoto-rgb 值转换为 CIE D50 XYZ // 矩阵不能用有理数形式表示,但按 64 位精度计算 // 参见 https://github.com/w3c/csswg-drafts/issues/7675 var M= [ [ 0.79776664490064230 , 0.13518129740053308 , 0.03134773412839220 ], [ 0.28807482881940130 , 0.71183523424187300 , 0.00008993693872564 ], [ 0.00000000000000000 , 0.00000000000000000 , 0.82510460251046020 ] ]; return multiplyMatrices( M, rgb); } function XYZ_to_lin_ProPhoto( XYZ) { // 将 D50 XYZ 转换为线性光 prophoto-rgb var M= [ [ 1.34578688164715830 , - 0.25557208737979464 , - 0.05110186497554526 ], [ - 0.54463070512490190 , 1.50824774284514680 , 0.02052744743642139 ], [ 0.00000000000000000 , 0.00000000000000000 , 1.21196754563894520 ] ]; return multiplyMatrices( M, XYZ); } // a98-rgb 相关函数 function lin_a98rgb( RGB) { // 将 a98-rgb 数组(范围 0.0 - 1.0)转换为线性光形式 // 现在也接受负值 return RGB. map( function ( val) { let sign= val< 0 ? - 1 : 1 ; let abs= Math. abs( val); return sign* Math. pow( abs, 563 / 256 ); }); } function gam_a98rgb( RGB) { // 将线性光 a98-rgb 数组(范围 0.0-1.0)转换为伽马校正形式 // 也接受负值 return RGB. map( function ( val) { let sign= val< 0 ? - 1 : 1 ; let abs= Math. abs( val); return sign* Math. pow( abs, 256 / 563 ); }); } function lin_a98rgb_to_XYZ( rgb) { // 将线性光 a98-rgb 转换为 CIE XYZ // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html // 比 Adobe RGB 1998 文档第4.3.5.3节具有更高的数值精度 // 以下值由 R G B W 的色度坐标从头推导计算得出 // 参见 matrixmaker.html var M= [ [ 573536 / 994567 , 263643 / 1420810 , 187206 / 994567 ], [ 591459 / 1989134 , 6239551 / 9945670 , 374412 / 4972835 ], [ 53769 / 1989134 , 351524 / 4972835 , 4929758 / 4972835 ], ]; return multiplyMatrices( M, rgb); } function XYZ_to_lin_a98rgb( XYZ) { // 将 XYZ 转换为线性光 a98-rgb var M= [ [ 1829569 / 896150 , - 506331 / 896150 , - 308931 / 896150 ], [ - 851781 / 878810 , 1648619 / 878810 , 36519 / 878810 ], [ 16779 / 1248040 , - 147721 / 1248040 , 1266979 / 1248040 ], ]; return multiplyMatrices( M, XYZ); } // Rec. 2020 相关函数 function lin_2020( RGB) { // 将 rec2020 RGB 数组(范围 0.0 - 1.0)转换为线性光形式 // 参考电光传递函数来自 Rec. ITU-R BT.1886 附录1 // b(黑电平提升)= 0,a(用户增益)= 1 // 在扩展范围上定义,不进行裁剪 return RGB. map( function ( val) { let sign= val< 0 ? - 1 : 1 ; let abs= Math. abs( val); >return sign* Math. pow( abs, 2.4 ); }); } function gam_2020( RGB) { // 将线性光 rec2020 RGB 数组(范围 0.0-1.0)转换为伽马校正形式 // 参考电光传递函数来自 Rec. ITU-R BT.1886 附录1 // b(黑电平提升)= 0,a(用户增益)= 1 // 在扩展范围上定义,不进行裁剪 return RGB. map( function ( val) { let sign= val< 0 ? - 1 : 1 ; let abs= Math. abs( val); >return sign* Math. pow( abs, 1 / 2.4 ); }); } function lin_2020_to_XYZ( rgb) { // 将线性光 rec2020 值转换为 CIE XYZ // 使用 D65(不做色度适应) var M= [ [ 63426534 / 99577255 , 20160776 / 139408157 , 47086771 / 278816314 ], [ 26158966 / 99577255 , 472592308 / 697040785 , 8267143 / 139408157 ], [ 0 / 1 , 19567812 / 697040785 , 295819943 / 278816314 ], ]; // 实际上 0 是计算得出的 4.994106574466076e-17 return multiplyMatrices( M, rgb); } function XYZ_to_lin_2020( XYZ) { // 将 XYZ 转换为线性光 rec2020 var M= [ [ 30757411 / 17917100 , - 6372589 / 17917100 , - 4539589 / 17917100 ], [ - 19765991 / 29648200 , 47925759 / 29648200 , 467509 / 29648200 ], [ 792561 / 44930125 , - 1921689 / 44930125 , 42328811 / 44930125 ], ]; return multiplyMatrices( M, XYZ); } // 色度适应 function D65_to_D50( XYZ) { // 使用 Bradford 方法从 D65 适应到 D50 // 下列矩阵是三步操作的结果: // - 从 XYZ 转换到视网膜锥体域 // - 将分量按参考白点从一个缩放到另一个 // - 再转换回 XYZ // 参见 https://github.com/LeaVerou/color.js/pull/354/files var M= [ [ 1.0479297925449969 , 0.022946870601609652 , - 0.05019226628920524 ], [ 0.02962780877005599 , 0.9904344267538799 , - 0.017073799063418826 ], [ - 0.009243040646204504 , 0.015055191490298152 , 0.7518742814281371 ] ]; return multiplyMatrices( M, XYZ); } function D50_to_D65( XYZ) { // 使用 Bradford 方法从 D50 适应到 D65 // 参见 https://github.com/LeaVerou/color.js/pull/360/files var M= [ [ 0.955473421488075 , - 0.02309845494876471 , 0.06325924320057072 ], [ - 0.0283697093338637 , 1.0099953980813041 , 0.021041441191917323 ], [ 0.012314014864481998 , - 0.020507649298898964 , 1.330365926242124 ] ]; return multiplyMatrices( M, XYZ); } // CIE Lab 和 LCH function XYZ_to_Lab( XYZ) { // 假定 XYZ 是相对于 D50 的,转换为 CIE Lab // 根据 CIE 标准,现定义为有理分数形式 var ε= 216 / 24389 ; // 6^3/29^3 var κ= 24389 / 27 ; // 29^3/3^3 // 计算 xyz,即相对于参考白点缩放的 XYZ var xyz= XYZ. map(( value, i) => value/ D50[ i]); // 现在计算 f var f= xyz. map( value=> value> ε? Math. cbrt( value) : ( κ* value+ 16 ) / 116 ); return [ ( 116 * f[ 1 ]) - 16 , // L 500 * ( f[ 0 ] - f[ 1 ]), // a 200 * ( f[ 1 ] - f[ 2 ]) // b ]; // L 在 [0,100] 范围。用于 CSS 时乘以 100 并加上百分号 } function Lab_to_XYZ( Lab) { // 将 Lab 转换为 D50 适配的 XYZ // http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html var κ= 24389 / 27 ; // 29^3/3^3 var ε= 216 / 24389 ; // 6^3/29^3 var f= []; // 计算 f,从亮度相关项开始 f[ 1 ] = ( Lab[ 0 ] + 16 ) / 116 ; f[ 0 ] = Lab[ 1 ] / 500 + f[ 1 ]; f[ 2 ] = f[ 1 ] - Lab[ 2 ] / 200 ; // 计算 xyz var xyz= [ Math. pow( f[ 0 ], 3 ) > ε? Math. pow( f[ 0 ], 3 ) : ( 116 * f[ 0 ] - 16 ) / κ, Lab[ 0 ] > κ* ε? Math. pow(( Lab[ 0 ] + 16 ) / 116 , 3 ) : Lab[ 0 ] / κ, Math. pow( f[ 2 ], 3 ) > ε? Math. pow( f[ 2 ], 3 ) : ( 116 * f[ 2 ] - 16 ) / κ]; // 将 xyz 乘以参考白点以计算 XYZ return xyz. map(( value, i) => value* D50[ i]); } function Lab_to_LCH( Lab) { var epsilon= 0.0015 ; var chroma= Math. sqrt( Math. pow( Lab[ 1 ], 2 ) + Math. pow( Lab[ 2 ], 2 )); // 色度 var hue= Math. atan2( Lab[ 2 ], Lab[ 1 ]) * 180 / Math. PI; if ( hue< 0 ) { hue= hue+ 360 ; } if ( chroma<= epsilon) { hue= NaN ; } return [ Lab[ 0 ], // L 保持不变 chroma, // 色度 hue// 色相,度数范围 [0 到 360) ]; } function LCH_to_Lab( LCH) { // 从极坐标形式转换 return [ LCH[ 0 ], // L 保持不变 LCH[ 1 ] * Math. cos( LCH[ 2 ] * Math. PI/ 180 ), // a LCH[ 1 ] * Math. sin( LCH[ 2 ] * Math. PI/ 180 ) // b ]; } // OKLab 与 OKLCH // https://bottosson.github.io/posts/oklab/ // 为了参考白点一致重新计算的 XYZ <-> LMS 矩阵 // 见 https://github.com/w3c/csswg-drafts/issues/6642#issuecomment-943521484 // 以 64 位精度重新计算 // 见 https://github.com/color-js/color.js/pull/357 function XYZ_to_OKLab( XYZ) { // 给定相对于 D65 的 XYZ,转换为 OKLab var XYZtoLMS= [ [ 0.8190224379967030 , 0.3619062600528904 , - 0.1288737815209879 ], [ 0.0329836539323885 , 0.9292868615863434 , 0.0361446663506424 ], [ 0.0481771893596242 , 0.2642395317527308 , 0.6335478284694309 ] ]; var LMStoOKLab= [ [ 0.2104542683093140 , 0.7936177747023054 , - 0.0040720430116193 ], [ 1.9779985324311684 , - 2.4285922420485799 , 0.4505937096174110 ], [ 0.0259040424655478 , 0.7827717124575296 , - 0.8086757549230774 ] ]; var LMS= multiplyMatrices( XYZtoLMS, XYZ); // JavaScript Math.cbrt 返回带符号的立方根 // 如果移植到其它语言请小心 // 尤其不要使用通用的幂函数替代 return multiplyMatrices( LMStoOKLab, LMS. map( c=> Math. cbrt( c))); // L 在 [0,1] 范围。用于 CSS 时乘以 100 并加上百分号 } function OKLab_to_XYZ( OKLab) { // 给定 OKLab,转换为相对于 D65 的 XYZ var LMStoXYZ= [ [ 1.2268798758459243 , - 0.5578149944602171 , 0.2813910456659647 ], [ - 0.0405757452148008 , 1.1122868032803170 , - 0.0717110580655164 ], [ - 0.0763729366746601 , - 0.4214933324022432 , 1.5869240198367816 ] ]; var OKLabtoLMS= [ [ 1.0000000000000000 , 0.3963377773761749 , 0.2158037573099136 ], [ 1.0000000000000000 , - 0.1055613458156586 , - 0.0638541728258133 ], [ 1.0000000000000000 , - 0.0894841775298119 , - 1.2914855480194092 ] ]; var LMSnl= multiplyMatrices( OKLabtoLMS, OKLab); return multiplyMatrices( LMStoXYZ, LMSnl. map( c=> c** 3 )); } function OKLab_to_OKLCH( OKLab) { var epsilon= 0.000004 ; var hue= Math. atan2( OKLab[ 2 ], OKLab[ 1 ]) * 180 / Math. PI; var chroma= Math. sqrt( OKLab[ 1 ] ** 2 + OKLab[ 2 ] ** 2 ); if ( hue< 0 ) { hue= hue+ 360 ; } if ( chroma<= epsilon) { hue= NaN ; } return [ OKLab[ 0 ], // L 保持不变 chroma, hue]; } function OKLCH_to_OKLab( OKLCH) { return [ OKLCH[ 0 ], // L 保持不变 OKLCH[ 1 ] * Math. cos( OKLCH[ 2 ] * Math. PI/ 180 ), // a OKLCH[ 1 ] * Math. sin( OKLCH[ 2 ] * Math. PI/ 180 ) // b ]; } // 预乘 alpha 的转换 function rectangular_premultiply( color, alpha) { // 给定矩形正交色彩空间中的颜色和 alpha 值 // 返回预乘形式 return color. map(( c) => c* alpha) } function rectangular_un_premultiply( color, alpha) { // 给定矩形正交色彩空间中预乘的颜色和 alpha 值 // 返回实际颜色 if ( alpha=== 0 ) { return color; // 避免除零 } return color. map(( c) => c/ alpha) } function polar_premultiply( color, alpha, hueIndex) { // 给定柱坐标/极坐标色彩空间中的颜色和 alpha 值 // 返回预乘形式 // hueIndex 表示颜色数组中哪一项是色相角 // 例如在 OKLCH 中为 2, 在 HSL 中为 0 return color. map(( c, i) => c* ( hueIndex=== i? 1 : alpha)) } function polar_un_premultiply( color, alpha, hueIndex) { // 给定柱坐标/极坐标色彩空间中的颜色和 alpha 值 // 返回实际颜色 // hueIndex 表示颜色数组中哪一项是色相角 // 例如在 OKLCH 中为 2, 在 HSL 中为 0 if ( alpha=== 0 ) { return color; // 避免除零 } return color. map(( c, i) => c/ ( hueIndex=== i? 1 : alpha)) } // 可以方便地定义一些便捷函数,例如 function hsl_premultiply( color, alpha) { return polar_premultiply( color, alpha, 0 ); }
20. 色差公式
本节为非规范性内容。
测试
本节为非规范性内容,不需要测试。
20.1. 色差度量简介。
色差公式用于估算两种颜色在视觉上有多大差异。 它可用于质量保证 (衡量再现的颜色是否正确), 或用于某些类型的色域映射, 在这种情况下,会最小化 超出色域的颜色与色域内颜色之间的色差。
从历史上看,这类色差公式 通称为 ΔE(delta E); 其中 E 是 Empfindung 的缩写, 这是德语中的“感觉”一词。
最简单的色差度量 ΔE76, 就是 CIE Lab 颜色空间中的欧几里得距离, 它于 1976 年首次成为国际标准。
虽然 ΔE76 是一个不错的初步近似, 但随着 CIE Lab 的局限性逐渐显现, 印刷和织物染色等对颜色要求严格的行业 很快便开发出了改进的公式。
随着公式变得更加复杂, 实际实现需要在 预测准确性 与计算复杂度(因而也包括速度)之间进行权衡。
20.2. ΔE2000
目前,对于标准动态范围颜色, 使用最广泛的公式 是 ΔE2000。 与 ΔE76 相比, 它修正了若干已知的不对称性和非线性。 由于该公式较为复杂, 并且关键依赖于 各种中间计算结果的符号, 实现往往会出现错误 [Sharma]。
下面的示例代码已经 验证, 与 [Sharma] 发布的成对 Lab 值及预期 ΔE2000 测试套件相比, 精确到五位有效数字, 并且是正确的。
// deltaE2000 在统计学上是一个显著的改进 // 优于 deltaE76 和 deltaE94, // 并受到 CIE 和 Idealliance 的推荐 // 尤其适用于小于 10 deltaE76 的色差 // 但它复杂得可怕 // 而且许多实现都有细微错误! /** * @param {number[]} reference - CIE Lab 值数组:L 为 0..100,a 和 b 约为 -150..150 * @param {number[]} sample - CIE Lab 值数组:L 为 0..100,a 和 b 约为 -150..150 * @return {number} 颜色样本与参考颜色的差异程度 */ function deltaE2000( reference, sample) { // 给定一个参考颜色和一个样本颜色, // 两者均采用 CIE Lab, // 计算 deltaE 2000。 // 此实现假定参数化 // 加权因子 kL、kC 和 kH // (用于表示观察条件的影响) // 均为 1,这似乎是通常的情况。 let [ L1, a1, b1] = reference; let [ L2, a2, b2] = sample; let C1= Math. sqrt( a1** 2 + b1** 2 ); let C2= Math. sqrt( a2** 2 + b2** 2 ); let Cbar= ( C1+ C2) / 2 ; // 平均色度 // 根据平均色度计算 a 轴不对称因子 // 这会将近中性色的 JND 椭圆重新变为圆形 let C7= Math. pow( Cbar, 7 ); const Gfactor= Math. pow( 25 , 7 ); let G= 0.5 * ( 1 - Math. sqrt( C7/ ( C7+ Gfactor))); // 使用不对称因子缩放 a 轴 // 顺便说一下,这就是不存在 Lab2000 颜色空间的原因 let adash1= ( 1 + G) * a1; let adash2= ( 1 + G) * a2; // 根据缩放后的 a 轴和原始 b 轴计算新的色度 let Cdash1= Math. sqrt( adash1** 2 + b1** 2 ); let Cdash2= Math. sqrt( adash2** 2 + b2** 2 ); // 计算新的色相,真正的中性色的色相为零 // 并使用度而非弧度 const π= Math. PI; const r2d= 180 / π; const d2r= π/ 180 ; let h1= ( adash1=== 0 && b1=== 0 ) ? 0 : Math. atan2( b1, adash1); let h2= ( adash2=== 0 && b2=== 0 ) ? 0 : Math. atan2( b2, adash2); if ( h1< 0 ) { h1+= 2 * π; } if ( h2< 0 ) { h2+= 2 * π; } h1*= r2d; h2*= r2d; // 明度和色度差;符号很重要 let ΔL= L2- L1; let ΔC= Cdash2- Cdash1; // 色相差,注意确保符号正确 let hdiff= h2- h1; let hsum= h1+ h2; let habs= Math. abs( hdiff); let Δh; if ( Cdash1* Cdash2=== 0 ) { Δh= 0 ; } else if ( habs<= 180 ) { Δh= hdiff; } else if ( hdiff> 180 ) { Δh= hdiff- 360 ; } else if ( hdiff< - 180 ) { Δh= hdiff+ 360 ; } else { console. log( "the unthinkable has happened" ); } // 加权色相差,色度越大权重越大 let ΔH= 2 * Math. sqrt( Cdash2* Cdash1) * Math. sin( Δh* d2r/ 2 ); // 计算平均明度和色度 let Ldash= ( L1+ L2) / 2 ; let Cdash= ( Cdash1+ Cdash2) / 2 ; let Cdash7= Math. pow( Cdash, 7 ); // 补偿 Lab 蓝色区域中的非线性。 // 色相加权因子有四种可能情况, // 具体取决于角度,以获得正确的符号 let hdash; if ( Cdash1* Cdash2=== 0 ) { hdash= hsum; } else if ( habs<= 180 ) { hdash= hsum/ 2 ; } else if ( hsum< 360 ) { hdash= ( hsum+ 360 ) / 2 ; } else { hdash= ( hsum- 360 ) / 2 ; } // 针对 CIELAB 非均匀性的局部修正 // 这些修正都是为了让 JND 椭球更接近球体 // SL 明度锐化因子 // 假定背景的 L=50 let lsq= ( Ldash- 50 ) ** 2 ; let SL= 1 + (( 0.015 * lsq) / Math. sqrt( 20 + lsq)); // SC 色度因子,类似于 CMC 和 deltaE 94 公式中的因子 let SC= 1 + 0.045 * Cdash; // 用于蓝色非线性的交叉项 T let T= 1 ; T-= ( 0.17 * Math. cos(( hdash- 30 ) * d2r)); T+= ( 0.24 * Math. cos( 2 * hdash* d2r)); T+= ( 0.32 * Math. cos((( 3 * hdash) + 6 ) * d2r)); T-= ( 0.20 * Math. cos((( 4 * hdash) - 63 ) * d2r)); // SH 色相因子取决于色度, // 也取决于像 deltaE94 那样调整后的色相角。 let SH= 1 + 0.015 * Cdash* T; // RT 色相旋转项用于补偿 JND 椭圆的旋转 // 以及孟塞尔等色相线的旋转 // 位于中高色度的蓝色区域中 // (色相 225 到 315) let Δθ= 30 * Math. exp( - 1 * ((( hdash- 275 ) / 25 ) ** 2 )); let RC= 2 * Math. sqrt( Cdash7/ ( Cdash7+ Gfactor)); let RT= - 1 * Math. sin( 2 * Δθ* d2r) * RC; // 最后逐项计算 deltaE,即平方和的平方根 let dE= ( ΔL/ SL) ** 2 ; dE+= ( ΔC/ SC) ** 2 ; dE+= ( ΔH/ SH) ** 2 ; dE+= RT* ( ΔC/ SC) * ( ΔH/ SH); return Math. sqrt( dE); // 太棒了!!! };
20.3. ΔEOK
由于 Oklab 不存在 CIE Lab 的色相线性、色相均匀性 和色度非线性问题, 因此色差度量无需对此进行修正, 所以 ΔEOK 就是 Oklab 颜色空间中的欧几里得距离。
// 计算 deltaE OK // 简单的平方和的平方根 /** * @param {number[]} reference - OKLab 值数组:L 为 0..1,a 和 b 为 -1..1 * @param {number[]} sample - OKLab 值数组:L 为 0..1,a 和 b 为 -1..1 * @return {number} 颜色样本与参考颜色的差异程度 */ function deltaEOK( reference, sample) { let [ L1, a1, b1] = reference; let [ L2, a2, b2] = sample; let ΔL= L1- L2; let Δa= a1- a2; let Δb= b1- b2; return Math. sqrt( ΔL** 2 + Δa** 2 + Δb** 2 ); }
20.4. ΔEOK2
作为一种色差度量,ΔEOK 与明度差异相比, 会低估彩度差异。 实验表明, 将 缩放 a 和 b 的倍数设为 2, 与 ΔE2000 相比, 能显著提高 基于 Oklab 的 ΔE 的预测准确性。 与其修改如今已被广泛采用的 Oklab 定义, 不如由此得到另一种距离度量 ΔEOK2。
// 计算 deltaE OK2 // 平方和的平方根,将 a 和 b 缩放 2 倍 /** * @param {number[]} reference - OKLab 值数组:L 为 0..1,a 和 b 为 -1..1 * @param {number[]} sample - OKLab 值数组:L 为 0..1,a 和 b 为 -1..1 * @return {number} 颜色样本与参考颜色的差异程度 */ function deltaEOK2( reference, sample) { let [ L1, a1, b1] = reference; let [ L2, a2, b2] = sample; let ΔL= L1- L2; let Δa= 2 * ( a1- a2); let Δb= 2 * ( b1- b2); return Math. sqrt( ΔL** 2 + Δa** 2 + Δb** 2 ); }
20.5. ΔEOKr2
还可以进一步改进, 方法是修改明度轴, 为其 添加一个“趾部”。 当存在显著的白色适应刺激时, 这尤其有用 (例如,白色背景的浅色模式网页)。 它还会使明度曲线更接近 CIE Lab 的明度曲线。
虽然这比原始的 ΔEOK 稍微复杂一些, 但它与 ΔE2000 的一致性显著更高, 同时仍远比 ΔE2000 简单。
对于对性能敏感的实现, 建议使用 ΔEOKr2 作为色差度量。
// 计算 deltaE OKr2 // 平方和的平方根,加入明度趾部,将 a 和 b 缩放 2 倍 /** * @param {number[]} reference - OKLab 值数组:L 为 0..1,a 和 b 为 -1..1 * @param {number[]} sample - OKLab 值数组:L 为 0..1,a 和 b 为 -1..1 * @return {number} 颜色样本与参考颜色的差异程度 */ function deltaEOKr2( reference, sample) { let [ L1, a1, b1] = reference; let [ L2, a2, b2] = sample; L1= toe( L1); L2= toe( L2); let ΔL= L1- L2; let Δa= 2 * ( a1- a2); let Δb= 2 * ( b1- b2); return Math. sqrt( ΔL** 2 + Δa** 2 + Δb** 2 ); } // 添加明度趾部 // https://bottosson.github.io/posts/colorpicker/#intermission---a-new-lightness-estimate-for-oklab /** * @param {number} x - Oklab 明度 * @return {number} 加入趾部后的明度 */ function toe( x) { const K1= 0.206 ; const K2= 0.03 ; const K3= ( 1.0 + K1) / ( 1.0 + K2); return 0.5 * ( K3* x- K1+ Math. sqrt(( K3* x- K1) * ( K3* x- K1) + 4 * K2* K3* x)); }
附录 A:已废弃的 CSS 系统颜色
早期版本的 CSS 定义了若干额外的系统颜色。 但是,这些颜色关键字已被弃用, 因为它们不足以实现其最初目的 (使网站元素看起来像其原生操作系统中的对应元素), 还会带来安全风险, 因为它们使网页更容易“伪装”原生操作系统对话框, 并扩大指纹识别范围,从而损害用户隐私。
用户代理必须支持这些关键字, 并且为了缓解指纹识别, 必须按照下表将它们映射到(未弃用的)系统颜色。 作者不得使用这些关键字。
已弃用的系统颜色表示为 <deprecated-color> 子类型, 并定义如下:
- ActiveBorder
- 活动窗口边框。计算为 ButtonBorder 的颜色。
- ActiveCaption
- 活动窗口标题栏。计算为 Canvas 的颜色。
- AppWorkspace
- 多文档界面的背景颜色。计算为 Canvas 的颜色。
- Background
- 桌面背景。计算为 Canvas 的颜色。
- ButtonHighlight
- 对于因一层外围边框而呈现三维效果的三维元素, 朝向光源一侧的边框颜色。计算为 ButtonFace 的颜色。
- ButtonShadow
- 对于因一层外围边框而呈现三维效果的三维元素, 背离光源一侧的边框颜色。计算为 ButtonFace 的颜色。
- CaptionText
- 标题栏、尺寸调整框和滚动条箭头框中的文本。计算为 CanvasText 的颜色。
- InactiveBorder
- 非活动窗口边框。计算为 ButtonBorder 的颜色。
- InactiveCaption
- 非活动窗口标题栏。计算为 Canvas 的颜色。
- InactiveCaptionText
- 非活动标题栏中的文本颜色。计算为 GrayText 的颜色。
- InfoBackground
- 工具提示控件的背景颜色。计算为 Canvas 的颜色。
- InfoText
- 工具提示控件的文本颜色。计算为 CanvasText 的颜色。
- Menu
- 菜单背景。计算为 Canvas 的颜色。
- MenuText
- 菜单中的文本。计算为 CanvasText 的颜色。
- Scrollbar
- 滚动条的灰色区域。计算为 Canvas 的颜色。
- ThreeDDarkShadow
- 对于因两层同心外围边框而呈现三维效果的三维元素, 背离光源的两条边框中较暗的一条(通常为外侧边框)的颜色。 计算为 ButtonBorder 的颜色。
- ThreeDFace
- 对于因两层同心外围边框而呈现三维效果的三维元素, 其表面的背景颜色。计算为 ButtonFace 的颜色。
- ThreeDHighlight
- 对于因两层同心外围边框而呈现三维效果的三维元素, 朝向光源的两条边框中较亮的一条(通常为外侧边框)的颜色。 计算为 ButtonBorder 的颜色。
- ThreeDLightShadow
- 对于因两层同心外围边框而呈现三维效果的三维元素, 朝向光源的两条边框中较暗的一条(通常为内侧边框)的颜色。 计算为 ButtonBorder 的颜色。
- ThreeDShadow
- 对于因两层同心外围边框而呈现三维效果的三维元素, 背离光源的两条边框中较亮的一条(通常为内侧边框)的颜色。 计算为 ButtonBorder 的颜色。
- Window
- 窗口背景。计算为 Canvas 的颜色。
- WindowFrame
- 窗口框架。计算为 ButtonBorder 的颜色。
- WindowText
- 窗口中的文本。计算为 CanvasText 的颜色。
测试
- deprecated-sameas-001.html (实时测试) (来源)
- deprecated-sameas-002.html (实时测试) (来源)
- deprecated-sameas-003.html (实时测试) (来源)
- deprecated-sameas-004.html (实时测试) (来源)
- deprecated-sameas-005.html (实时测试) (来源)
- deprecated-sameas-006.html (实时测试) (来源)
- deprecated-sameas-007.html (实时测试) (来源)
- deprecated-sameas-008.html (实时测试) (来源)
- deprecated-sameas-009.html (实时测试) (来源)
- deprecated-sameas-010.html (实时测试) (来源)
- deprecated-sameas-011.html (实时测试) (来源)
- deprecated-sameas-012.html (实时测试) (来源)
- deprecated-sameas-013.html (实时测试) (来源)
- deprecated-sameas-014.html (实时测试) (来源)
- deprecated-sameas-015.html (实时测试) (来源)
- deprecated-sameas-016.html (实时测试) (来源)
- deprecated-sameas-017.html (实时测试) (来源)
- deprecated-sameas-018.html (实时测试) (来源)
- deprecated-sameas-019.html (实时测试) (来源)
- deprecated-sameas-020.html (实时测试) (来源)
- deprecated-sameas-021.html (实时测试) (来源)
- deprecated-sameas-022.html (实时测试) (来源)
- deprecated-sameas-023.html (实时测试) (来源)
附录 B:已废弃的古怪十六进制颜色
当 CSS 在 怪异模式下解析时, <quirky-color> 是一种 <color> 类型, 仅在某些属性中有效:
它在包含或引用这些属性的属性中不有效, 例如 background 简写属性, 或者在 函数记法 内部, 例如 color-mix()
此外,虽然 <quirky-color> 在 <color>
语法中必须在解析受影响属性的 @supports 规则时有效,
但在 CSS.supports()
方法中用于这些属性时,
它不有效。
根据以下规则, <quirky-color> 可以表示为 <number-token>、<dimension-token> 或 <ident-token>:
-
如果它是一个 <ident-token>, 该标记的表示必须恰好包含 3 或 6 个字符, 且全部为十六进制数字。 它表示一个具有相同值的 <hex-color>。
-
如果它是一个 <number-token>, 它必须设置整数标志。
序列化该整数的值。 如果序列化结果少于 6 个字符, 就在前面补上 "0" 字符, 直到它长为 6 个字符。 它表示一个具有相同值的 <hex-color>。
-
如果它是一个 <dimension-token>, 它必须设置整数标志。
序列化该整数的值, 并附加该标记单位的表示。 如果结果少于 6 个字符, 就在前面补上 "0" 字符, 直到它长为 6 个字符。 它表示一个具有相同值的 <hex-color>。
(换句话说,怪异模式允许不写前导 "#" 来书写十六进制颜色, 但解析规则很奇怪。)
Tests
quirky hex colors
致谢
除了那些为 CSS Color 3 做出贡献的人之外,编辑们还要感谢 Emilio Cobos Álvarez、Alexey Ardov、Chris Bai、Amelia Bellamy-Royds、Lars Borg、Mike Bremford、Andreu Botella、Dan Burzo、Max Derhak、fantasai、Simon Fraser、Devon Govett、Phil Green、Dean Jackson、Andreas Kraushaar、Pierre-Anthony Lemieux、Tiaan Louw、Cameron McCormack、Romain Menke、Chris Murphy、Isaac Muse、 Jonathan Neal、Chris Needham、Björn Ottosson、Christoph Päper、Brad Pettit、Xidorn Quan、Craig Revie、 Melanie Richards、Florian Rivoal、Jacob Rus、Joseph Salowey、Simon Sapin、Igor Snitkin、Lea Verou、Mark Watson、James Stuckey Weber、Sam Weinig 和 Natalie Weizenbaum。
变更记录
自 2025年4月24日 候选推荐草案 以来的变更
- 澄清了有关等效颜色的措辞,并添加了许多详细示例
- 澄清了颜色空间转换产生的中间值不会被钳制,以实现无误差的 往返转换 议题 9484
- 色域映射既适用于 unorm8 画布,也适用于显示器 (议题 10112)
- 澄清了相对色度渲染适用于渐变,而感知渲染适用于摄影图像 (议题 14051)
- 解释了颜色插值中进度与量之间的关系。精确定义了线性 插值。 (议题 14201)
- 转换期间,当色相无效时,强制将色度设为零 (议题 14133)
- 阐明了插值中的各个步骤及其影响 (议题 14347)
- 更好地说明了色差度量,并添加了 ΔΕOKr2 (Issue 14207)
- 为 sRGB、display-p3、a98-rgb、prophoto-rgb 和 rec2020 预定义色彩空间添加了 Oklab a、b 平面图,并与现有的 CIE Lab 图并列
- 避免提及“为转换做准备”,直接陈述即可 (议题 14049)
- 澄清了已弃用系统颜色的计算值 是相应的(未弃用)系统颜色的颜色 (议题 3873、 议题 13459>、 议题 13719)
- 整合了系统颜色和已弃用系统颜色的解析 (议题 13450)
- 阐明了颜色插值中的延续处理 (议题 14134)
- 根据使用的是传统语法还是现代语法拆分 alpha 值的序列化;要求使用 16 位 alpha (议题 13994)
- 在 color(...) 语法中统一提及 display-p3-linear (PR 14024)
- 重写 color-scheme 一节,以使用“页面配色方案”和“元素配色方案”的概念 (议题 13857)
- 根据是否存在 none 选择 sRGB 序列化形式 (议题 10254)
- 对于 Oklab 中的颜色比较,将 ε 标准化为 0.00001 (议题 13157)
- 新增一节,定义两个 <color> 值何时是等效颜色, 涵盖同一颜色空间中的分量比较、对缺失分量的处理, 以及通过 oklab 进行跨颜色空间比较。 (议题 13157)
- 在主要的颜色插值一节中阐明,如果未指定色相插值方法, 则默认使用较短路径。(色相插值一节中此前已作出此规定。) (议题 13788)
- 将类似分量的概念扩展为类似分量集合,以尽量减少 none → 0 转换 (议题 10210)
- 将颜色转换拆分为两个阶段 (议题 10211)
- 阐明了系统颜色如何响应所使用的配色方案 (议题 13719)
- 更新摘要,以提及颜色插值和色域映射。
- 阐明有关 CSS 色域映射目标的措辞
- 修正光线追踪算法,使其不会覆盖 end (议题 10579)
- 为光线追踪色域映射算法添加伪代码 (议题 10579)
- 添加 EdgeSeeker 和光线追踪色域映射算法。允许从三种 GMA 中选择 (议题 10579)
- 添加一幅展示 CIE Lab 中虚构颜色的图
- 区分超出色域但物理上可实现的颜色与虚构颜色
- 对裁剪作出更加客观的说明, 展示了一些能够产生可接受结果的情况 (议题 10579)
- 修复 ΔE2000 示例实现中的不一致 (议题 13322)
- 更新 AccentColor,使其从 accent-color 获取值,除非处于强制颜色模式 (议题 5900)
- 将 rec2020 颜色空间定义为使用以显示为参考的 2.4 gamma (议题 12574)
- 将 display-p3-linear 添加到预定义颜色空间 (议题 11250)
- 阐明使用 calc() 序列化不透明度值的方式 (议题 10426)
- 为实现兼容性,传统 sRGB 颜色之间的插值(再次)在 sRGB 空间中进行 (议题 7949)
- 阐明现实世界中 CIE Lab 的 a 和 b 范围 (议题 12208)
- 阐明 Opacity 值不会影响命中测试 (议题 11339)
自 2024 年 2 月 13 日候选推荐草案以来的变更
- 澄清了在 color 属性内部,使用的是已解析的继承值(而不是原始继承值)
- 列举了颜色的分类,比如哪些颜色会解析为 sRGB,哪些支持旧式颜色语法
- 添加了色相标准化的示例
- 修正了对应分量表,原本缺少了 alpha,但正文中已经将其描述为对应分量
- 将不透明度值的序列化收集在同一章节,并加以澄清
- 完善了关于 color-interpolation-method 和 host syntax 的表述
- 定义了 epsilon,用于返回缺失的色相
- 对缺失色相的无彩色采样采用了更精确的定义(只要足够接近中心轴即可)
- 统一使用“color component”(颜色分量)而不是“color channel”(这两个词之前都有用)
- 相关色温(Correlated Color Temperature)原本没有定义或解释,现已补充参考资料
- 导出了“预乘(premultiplied)”术语,并统一链接到该术语
- 弃用和未弃用的系统颜色被视为等价已不再属于“风险项”
- 澄清了“解析 CSS <color>”算法的预期用途
- 增加了 HTML 兼容序列化的修正版示例
- 去除了 HTML 兼容序列化关于缺失值的检查,因为它们会被预先转换为 0
- 将关于缺失值变为 0 的说明移至适用于 HTML 兼容与 CSS 序列化的两类情况
- 为 sRGB 增加了一个 HTML 兼容的十六进制序列化方式
- 添加了另一个 xyz-d65 和 xyz-d50 的示例
- 澄清 XYZ 中哪个分量(Y)对应亮度
- 明确 CSS 色域映射作用于实际值(actual value),而不是使用值(used value)
- 移除 hslToRgb 示例代码里的色相归一化,因为输入已在解析时规范化
- 修正色域映射算法第4步的伪代码
- 澄清插值是组合两种颜色最常见的场景,但不是唯一情况
- 确保 deltaE 表中的文本对比度足够
- 移除所有 <absolute-color-function> 用法,为保持一致采用 <absolute-color> function
- 更新了致谢部分
- 增加了色域网格示意图
- 用声明值(declared value)而不是指定值(specified value)描述 CSSOM 的序列化
- 增加了“亮度(luminance)”的导出定义
- 补充了 <opacity-value> 的产生式规则
- 阐明 hslToRgb 的结果何时会在 [0,1] 区间
- 澄清线性化后,RGB 空间具有加色特性
自 2022 年 11 月 1 日的候选推荐草案以来的更改
- 添加了序列化 uint8_t alpha 的步骤,从 cssom-1 中移动
- 恢复了解析时对 HSL 负饱和度的钳制为 0,这是 CSS Color 3 的当前互操作行为
- 插值时,始终转换颜色空间,以便无效的组件成为缺失的组件
- 澄清了何时从序列化中省略 alpha 为 1
- 移除了对色相角度的重复约束为 [0,360],因为这已经完成了
- 修正了 ActiveCaption 的描述,它是一个背景
- 区分了不透明度和 alpha。不透明度属性现在使用 opacity-value(其钳制行为与 alpha-value 不同)
- 澄清了在预乘之前进行前向运算
- 更新了色域映射算法
- 修复了一些关于色相插值的问题
- 澄清了 HWB 的白色或黑色达到 100% 并不足以构成无色彩,关键是其总和
- 避免在 RGB 到 HSL 转换中返回负饱和度;调整色相指向“另一侧”
- 对 ProPhoto 使用 64 位精确矩阵,它没有有理数形式
- 重新计算 Oklab 矩阵以达到 64 位精度(与之前 32 位精度下的结果相同)
- 即使目标未被限定,在色域映射中仍然一致地返回目标颜色空间的 GMA 输出
- 添加了解释为什么 Oklab 的一个 JND 为 0.02 而不是 2
- 澄清了解析 sRGB 值不适用于 color() 函数
- 将 alpha 值定义上移至不透明度属性,澄清不透明度指定值不被钳制
- 系统颜色现在明确允许伪装,以保护隐私
- 修正了 D50 到 D65 的逆色度适应矩阵
- 一致区分线性 Bradford 和原始、更复杂的 Bradford 色度适应算法
- 在色域映射算法中,返回剪裁后的结果,避免不必要的步骤
- 将色度适应矩阵更新为更高精度
- 添加了“解析 css 颜色”的算法,以便非 CSS 规范使用颜色时不必重新发明机制
- 澄清几何色域映射不能将色度投射回原始颜色之外
- 在色域映射讨论中使用术语“几何”而不是“分析”
- 将 HSL 的叙述与语法保持一致(百分比和数字均可接受)
- 修正了 LCH alpha 插值示例,错误地未预乘色相角度
- 修正了 sRGB 和 display-p3 传递函数。(仅当一个分量具有精确值 10.31475 / 255 时才会影响结果,这在每分量 8 或 10 位的情况下是不可能的)
- 澄清了系统颜色的指定值仍然是它们本身
- 添加了对 PNG cICP 块标记图像的提及
- 描述了传递 0/360 时色相增加和减少的行为
- 将 HSL 中无力的描述与其他极坐标颜色模型对齐
- 明确定义了颜色插值的操作顺序
- 添加了关于 calc() 中退化数值常量的提及
- 澄清了 sRGB 中 calc() 具有早期解析和钳制结果的行为
- 澄清了 HWB 色相具有与 HSL 色相相同的缺点
- 添加了亮度与明度的比较和图示
- 添加了色相插值关键字的描述和示例
- 对无色 HWB 颜色使用规范化叙述
- 修正了色相插值角度范围;[0,360) 而不是 [0,360]
- 表达了当 L=0% 或 100% 时显示为黑或白是由于色域映射。移除了错误的无力声明
- 移除了令人困惑的“表示黑色”和“表示白色”的评论
- 澄清了对手 a 和 b 是类比的
- 使用参考范围而不是叙述性描述来定义 RGB 组件,以保持一致性
- 明确参考了百分比转换为数字时 Lab、LCH、Oklab、OKLCh 序列化的百分比参考范围
- 要求 Oklab 插值,移除之前的“可以”,并描述了显式退出
- 将 Lab、LCH、Oklab 和 OKLCh 教程部分标记为非规范性。将一些定义移出非规范部分
- 澄清了插值时,在颜色空间转换之前进行类比组件的检查
- 从 CSS Color 5 回移了 hwb() 语法更改和参考范围
- 定义前向运算必须在无力操作之前进行
- 澄清了在传统 rgb() 语法中必须是全数字或全百分比的颜色组件
- 澄清了对于传统语法,颜色组件必须是全百分比或全数字
- 添加了在 calc() 中指定超出范围的 alpha 的示例
- 将带修剪后尾随零的颜色序列化示例放置到相关文本
- 澄清了 currentColor 的解析
- 更新了致谢部分
- 停止声称无色颜色具有缺失的 a、b 或色度
- 将 HSL 和 HWB 更改为无界色域,以促进来回转换
- 定义 HSL 的百分比参考范围
- 现代颜色语法 hsl() 和 hsla() 允许混合数字和百分比分量
- 现代颜色语法 rgb() 和 rgba() 允许混合数字和百分比分量
- 定义术语“现代颜色语法”(“传统颜色语法”已定义)
- 一致使用术语“类比组件”
- 更改为允许所有预定义的色彩空间进行插值
- 澄清了对于 color(),需要三个参数(RGB 或 XYZ)
- 澄清了命名颜色、系统颜色和透明的序列化
- 定义 Lab、LCH、Oklab、OKLCh 的指定值
- 定义其他 sRGB 颜色的指定值
- 定义命名颜色和系统颜色的指定值
- 在解析值时间钳制 alpha、明度、色度和色相
- 移除对高光白和 CIE 明度的提及
- 不再要求保留指定的色相;钳制到 [0, 360]
- 在示例中保持明度和数字的序列化一致性
- 轻微的拼写错误和编辑性澄清
自 2022 年 7 月 5 日的候选推荐草案以来的更改
- 移除了色相插值的“指定”值
- 更精确地定义了色相插值角度,保持 360 度的差异
- 添加了预乘运算中携带前向 alpha 的示例
- 澄清了 L=100% 时 a,b 和 C,h 无效,表示白色
- 移除了对 L=400 的随意提及,它适用于 hdr-CIELAB 而非 CIE Lab
- 一致地使用 Oklab 和 OKLCh 的大小写
- 将有效颜色、无效颜色、超出色域和在色域内的定义移动到术语部分
- 修正了“更长”色相插值的定义
- 进一步澄清了主机语法的概念
- 改进了颜色样本的可访问性
- 明确指出传统形式不支持“none”
- 从色相生成中移除了“none”,因为它在传统语法中不允许
- 移除了一些与 CMYK 和 CMYKOGV 相关的残余引用,移动到 CSS Color 5
- 澄清了插值颜色中缺失的值是如何前向传递的
- 更新了 xyz-params 的语法,使其接受数字和百分比,以与叙述保持一致
- 确保所有示例和图示都有 ID,自引用链接
- 澄清了对实现者来说阅读色域映射介绍的重要性
- 移除了剩余的自定义色彩空间提及(该功能已移至 CSS Color 5)
- 重构了 <color> 和 <alpha-value> 的语法
- 编辑性重构以获得更好的阅读顺序
- 更新了色域映射算法的伪代码,移除不必要的 deltaE 调用
自 2022 年 6 月 28 日的工作草案以来的更改
- 更新了候选推荐的状态
自 2022 年 4 月 28 日的工作草案以来的更改
- 将不透明度属性移动到模块顶部,紧邻颜色属性,进入详细内容之前
- 改进了对 color 属性的描述,特别是其对其他属性的影响
- 修正了等于模 360 颜色的更长色相调整方程
- 添加了两个新的系统颜色:AccentColor 和 AccentColorText
- 在新部分中描述了整体的颜色空间转换步骤
- 考虑了 none alpha 在预乘和反预乘中的影响
自 2021 年 12 月 15 日的工作草案以来的更改
- 使系统颜色完全解析,但禁止在强制颜色模式中更改它们
- 移除了对 color() 函数中参数数量不正确的宽容处理
- 将 CIE Lightness 和 OK Lightness 的序列化更改为数字而非百分比
- 将已废弃的系统颜色等价标记为风险项
- 为 CIE 和 OK L,a,b,C 的百分比值添加了参考范围
- 指出有用于执行和撤销预乘运算的示例代码,适用于矩形和极坐标颜色空间
- 为色域映射叙述以及伪代码添加了超出范围的钳制
- 添加了 ProPhoto RGB / ROMM 的规范性参考
- 修正了 sRGB 和 Display P3 参考围绕的黑点值
- 添加了 Display P3 的规范性参考
- 避免了在色域减少中出现无限循环,对比白色或比黑色更暗的颜色
- 澄清了 none 值的序列化
- 澄清了 Oklab 中非传统颜色的插值选择加入
- 定义了预乘运算的工作原理,涉及 none 值
- 澄清了在 RGB 中缺失的值序列化为 0
- 澄清了与 none 值一起使用 calc() 的行为
- 系统颜色中拼写错误和大小写不一致的问题
- 添加了 SelectedItem 和 SelectedItemText 的示例
- 明确提到遗留颜色的存在与否
- 添加了 CIE XYZ 的规范性参考
- 添加了 HWB 和 HSL 的规范性参考
- 澄清了 hwb() 不是传统语法,因此不支持旧的逗号分隔形式
- 澄清了只有遗留颜色会进行色域映射,其他颜色是无界的
- 使用不同术语,分光光度计和分光辐射计
- 修复了一些小拼写错误,并进行了语法改进
自 2021 年 6 月 1 日的工作草案以来的更改
- 添加了色域映射部分,并定义了 CSS 色域映射算法为 OKLCh 中的色度缩减,使用局部 MINDE
- color(xyz ...) 的计算值为 color(xyz-d65 ...)
- 将 srgb-linear 添加到插值颜色空间中
- 更新了来自 Colors 3 的更改部分
- 添加了解析 Oklab 和 OKLCh 值的部分
- 添加了 srgb-linear 颜色空间
- 根据 CSSWG 决议将 @color-profile 和 device-cmyk 移至 5 级
- 定义了插值颜色空间
- 澄清了矩阵是行优先的,并链接到矩阵乘法库
- 将旧的安全与隐私部分拆分为单独的部分
- 定义了怪癖模式下的古怪十六进制颜色
- 移除了 device-cmyk 中的后备颜色
- 未声明默认值的主机语法现在默认使用 Oklab
- 添加了 deltaE OK 的示例代码
- 添加了 OKlab 和 OKLCh 的示例转换代码
- 添加了 oklab() 和 oklch() 函数 添加了 Oklab 和 OKLCh 的描述
- 添加了 CIE LCH 的缺陷描述
- 允许颜色的所有组件通过 none 关键字“缺失”,定义了组件在某些情况下何时“无效”并自动变为缺失,并将所有对“NaN”组件的引用修正为使用“缺失”概念
- 定义了明确的 x,y 白点值,并在整个过程中保持一致
- 定义了主机语法的术语
- 定义了解决覆盖颜色上下文的背景
- 添加了一对新的系统颜色
- 修正了 HSL 和 HWB 的示例代码
- 用无错误版本替换了 HSL 值表
- 根据 WG 决议添加 Lea Verou 为共同编辑
- 澄清了色相角度是无界的
- 修正了 MarkText 示例
- 添加了图示,修正了示例
- 一些编辑性澄清
- 修正了一些小拼写错误和标记错误
自 2020 年 11 月 12 日的工作草案以来的更改
- 指出了接近中性的 Lab 值转换为 LCH 时色相不确定的问题
- 澄清了 RGB 与 Lab 相互转换中哪些步骤属于线性组合
- 向 @color-profile 添加了 components 描述符,供 CSS Color 5 使用
- 所有预定义 RGB 颜色空间都定义在扩展范围上
- 澄清了颜色插值之前不存在色域映射或色域裁剪步骤
- 澄清了旧式 sRGB 语法的插值
- 从 color() 中移除了 lab 选项
- 列出了预定义颜色空间之间相互转换的步骤
- 统一使用 color space(两个单词)这一术语
- 提供了更多关于选择用于混合的颜色空间的指导
- 重新计算了一个示例以提高精度
- 添加了色相插值示例
- 通过移除后备选项简化了 color() 语法
- 澄清了可从 @color-profile 链接的 ICC 配置文件类型
- 移除了对罕见 ICC 命名颜色的支持
- 提高了标准白点色度的精度
- 从一个预定义颜色空间的描述中移除了商标
- 重新表述了插值,使其在插值空间方面更加通用
- 更正了可访问性注意事项章节
- 澄清了 color() 的颜色空间参数是必需的,即使对于 sRGB 也是如此
- 澄清了 currentColor 并不限于 sRGB
- 对 sRGB 到 XYZ 再到 sRGB 的矩阵进行了小幅修正,以改善往返转换
- 澄清了 rec2020 传递函数,并引用了正确的 ITU Rec BT.2020-2 参考文献
- 更正了后备示例以使用正确的语法
- 不再强制非旧式颜色在伽马编码空间中进行插值
- 定义了预乘 alpha 插值
- 开始处理与 currentColor 之间的插值
- 定义了包含 NaN 的色相插值
- 泛化了颜色插值
- 定义插值在 Lab 中进行,并可覆盖为 LCG
- 修正了色相插值
- 定义了色相角插值
- 添加了插值章节
- 更正了一些示例中的语法
- 澄清了 color() 中究竟哪些分量允许使用百分比
- 更改为将 lch() 序列化为其自身,而不是序列化为 lab()
- color() 中的非旧式 sRGB 每个分量最低精度为 10 位
- color() 中的颜色空间不再是可选的
- 统一了 lab() 与 color(lab) 之间的最低精度
- 澄清了 color() 函数的后备过程——首先选择第一个有效且位于色域内的颜色,否则选择第一个有效 颜色并对其进行色域映射,否则使用透明黑色
- 澄清了 opacity 属性与带有不透明度的颜色之间的区别,尤其是在渲染重叠 文本字形时
- 添加了 ΔE2000 的示例代码(并已验证其正确性)
- 添加了此前未定义的术语色度的定义及示例;定义了色度图。
- 添加了颜色可加性的说明及示例
- 添加了 WPT 测试的源代码链接
- 导出了 color 和 valid color 的定义,以供其他规范引用
- 定义了序列化时每个分量的最小位数
- 更新了“适用于”定义(CSS 范围的更改)
- 为预定义颜色空间添加了图像状态(显示参照或场景参照)
- 列出了白点相关色温(例如 D65)以及色度坐标,以便 更加清晰
- 澄清了舍入方向为 +∞
- 修正了拼写错误、标记错误和链接
自 2019 年 11 月 5 日的工作草案以来的更改
- 导出一些术语以供其他规范使用
- 将要求从 WCAG 2.0 更新为 2.1
- 完整规定用于序列化的 Unicode 字符
- 定义特殊命名颜色的序列化
- 定义 device-cmyk() 的序列化
- 定义 color() 的序列化
- 以最大程度兼容 Web 的方式完整定义 RGB 序列化
- 定义 Lab 和 LCH 的序列化
- 完整定义 alpha 值的序列化
- 进行一致性检查,以避免意外使用 RFC2119 规范性措辞
- 为所有示例添加 ID,以便引用
- 将解析颜色和序列化颜色章节分开
- (安全性)ICC 配置文件不包含可执行代码
- 定义对于带配置文件的颜色而言超出范围意味着什么
- 澄清超出范围值的限制处理
- 添加指定值的示例
- 澄清计算值
- 通过对弃用的系统颜色实施强制映射来抵抗指纹识别
- 添加关于 X11 颜色标准化历史及原因的说明性注释
- 更正 hwb 示例代码
- 添加 MacBeth 色块的 DeltaE2000 值表
- 添加关于 ICC 配置文件互联网媒体类型的注释
- 添加对 PNG sRGB 块的引用
- 澄清 CMYK 到 Lab 的相互转换
- 澄清 RGB 到 Lab 的相互转换
- 增加 HSL 与 LCH 的比较
- 增加对 Rec BT.2020 颜色空间的描述
- 更新 prophoto-rgb 的描述
- 从值定义章节中移除重复的“关键字”
- 添加无效颜色的示例
- 添加包含多个后备项的示例
- 修正各种拼写错误和标记问题
- 澄清未声明自定义颜色空间的处理方式
- 澄清一些示例和说明性注释
- 规定有效和无效 ICC 配置文件的处理方式
- 定义带有显式标记颜色空间的图像的处理方式
- 定义 4k、SDR 视频的颜色空间
- 规定用户的对比度设置必须具有优先权
- 澄清强制颜色模式之外系统颜色的含义
- 更新默认样式规则
- 向 color() 添加 CIE XYZ 颜色空间
- 更清晰地说明色相角,并明确允许 NaN
- 改进系统颜色配对章节,要求达到 AA 级可访问对比度
- 警告重叠字形与 opacity 属性之间的相互影响
- 更正颜色定义中的语法
- 改进 Highlight/HighlightText 的描述
- 更正 prophoto-rgb 传递函数
- 提高 prophoto-rgb 原色的精度
- 开始定义“无法显示”
- 移除有关 canvas 表面的段落
- 添加 buttonborder、mark 和 marktext 系统颜色
- 添加反向转换,即从 sRGB 转换为 HWB
- 澄清极坐标空间是圆柱形的,而不是球形的
- 添加可访问性注意事项章节
- 开始描述通过降低色度进行色域映射,而不是逐分量裁剪
- 更正 rec2020 的白色色度
- 使 device-cmyk 可通过 @color-profile 使用;更新 CMYK 到颜色的算法,仅将朴素 转换作为最后手段
- 添加面向打印的 CMYK 和 KCMYOGV 示例
- 用户定义的颜色空间现在使用 dashed-ident,使预定义颜色空间可以扩展而不会发生冲突
- 向 color() 函数添加 lab 选项
- 添加 CIE Lab 的规范性引用
- 澄清 prophoto-rgb 使用 D50 白点,因此不需要进行适配
- 澄清 LCH 中角度增大的方向
- 澄清颜色名称对 ASCII 大小写不敏感
- "color" 属性的初始值现在为 CanvasText
- 根据 CSS WG 决议移除容易引起混淆的 gray() 函数
- 将分散的定义收集到新的 颜色术语章节中
- 添加有帮助的图示和更多示例
- 细微的编辑性澄清、拼写检查、拼写错误修正和 bikeshed 标记修正
自 2016 年 7 月 5 日的工作草案以来的更改
- 将 Lab 和 LCH 中的明度更改为百分比,以提高与 CSS 的兼容性
- 明确了颜色值的钳制
- 现在允许使用百分比透明度
- 定义术语 sRGB 和线性光 sRGB,供其他规范使用
- 添加了新的 CSS 系统颜色列表;将 Text 重命名为 CanvasText
- 使系统颜色关键字计算为它们自身
- 为系统颜色添加计算/使用条目
- 重写了未废弃系统颜色的介绍,使其使用集中于强制颜色模式而非通用使用
- 在预定义色彩空间中使用一致的连字符
- 恢复了关于非不透明元素在没有定位时仍然绘制在图层上的文本
- "color" 属性的初始值现在为黑色
- 澄清了 LCH 中的色相是模 360 度(现已撤销此更改)
- 澄清了 LCH 和 Lab 中 L 的允许范围及 L=100 的含义
- 更新了用于视频的色彩空间的参考
- 添加了 prophoto-rgb 预定义色彩空间
- 修正了 display-p3 的黑白亮度水平
- 澄清了 display-p3 的传递函数
- 添加了 a98-rgb 色彩空间,修正了原始色度的表格
- 澄清了 currentColor 的计算值不是解析后的颜色
- 更新了示例中的语法以符合最新规范
- 移除了 color-mod() 函数
- 从属性定义表中删除了“媒体”项
- 导出并一致使用“透明黑”和“不透明黑”
- 澄清了计算值(如百分比)
- 澄清了颜色组件所需的精度和四舍五入行为
- 澄清了“color”属性对颜色字体字形没有影响(除非特别引用,例如使用 currentColor)
- 澄清了颜色值如何解析
- 澄清了 HSL、HWB 和命名颜色解析为 sRGB
- 简化了从 device-cmyk 到 sRGB 的转换
- 将以前使用逗号的颜色语法描述为“遗留的”;将示例更改为不带逗号的形式
- 移除了要求显示的颜色必须限制在设备色域内的多余要求(似乎没有其他选项!)
- 将 P3 重命名为 display-p3;避免声称它是 DCI P3,因为它们并不相同
- 改进了对“color()”函数参数的描述
- 禁止在“@color-profile”标识符中使用预定义的色彩空间
- 为“color”、“color-adjust”和“opacity”属性定义添加规范顺序
- 将 alpha 合成定义从 SVG11 切换到 CSS 合成
- 澄清示例转换代码是非规范性的
- 添加了安全和隐私考虑
- 更新了多个参考文献至最新版本
- 将内联问题转换为 GitHub 问题的链接
- 一些小的编辑澄清、格式和标记改进
来自 Colors 3 的更改
与 CSS Color 3 相比,主要变化是 CSS 颜色不再局限于 sRGB 的狭窄色域。
为支持这一点,添加了几项全新的功能:
其他技术更改:
- <color> 的序列化现在在此处指定,而不是在 CSS 对象 模型中指定
- hwb() 函数, 用于以 HWB 表示法指定 sRGB 颜色。
- 添加命名颜色 rebeccapurple。
此外,还有一些语法更改:
- rgb() 和 rgba() 函数现在接受 <number> 而不是 <integer>。
- hsl() 和 hsla() 函数现在对于色相既接受 <angle>, 也接受 <number>。
- rgb() 和 rgba(),以及 hsl() 和 hsla() 现在 互为别名 (它们都具有可选的 alpha)。
- rgb()、rgba()、hsl() 和 hsla() 都 获得了一种新语法, 由空格分隔的参数 和可选的斜杠分隔不透明度组成。 现在所有颜色函数都使用这种语法形式, 以符合 CSS 的 函数表示法设计原则。
- <alpha-value> 的所有用法现在除了接受 <number> 外,也接受 <percentage>。
- 添加了 4 位和 8 位十六进制颜色,以指定透明度。
- 添加了 none 值,用于表示无作用分量。
21. 安全性考虑
系统颜色,如果它们实际上对应于用户的系统颜色,则会带来安全风险,因为它们使恶意软件网站更容易创建看起来像是来自系统的用户界面。然而,由于现在有几个系统颜色被定义为“通用”,因此认为此风险已得到缓解。
22. 隐私性考虑
本规范定义了“系统”颜色,理论上可能会暴露用户操作系统设置的详细信息,这是一种指纹识别风险。
23. 可访问性考虑
本规范鼓励作者不要仅使用颜色作为区分特征。
本规范鼓励浏览器确保特定系统颜色前景/背景对之间的足够对比度。曾考虑过使用具有特定 AA 或 AAA 对比度的更严格要求,但由于浏览器通常只是传递操作系统做出的颜色选择,或由用户选择(用户可能有特定要求,包括对偏头痛或癫痫发作患者降低对比度),CSSWG 无法要求特定的对比度水平。