渐变色生成器

在 sRGB、HSL、OKLCH 三个插值空间里计算色带,顺手给出可粘贴的 CSS 渐变代码、每个停止点的色值与三空间的逐档对照。

0%25%50%75%100%

三种插值空间 × 5 个停止点:共 15 行,当前显示 12 行。

插值空间位置色值RGB(0~255)HSL相对亮度 L
sRGB 直接插值0%#2563EB37, 99, 235221, 0.83, 0.530.1532
sRGB 直接插值25%#5972B389, 114, 179223, 0.37, 0.530.1741
sRGB 直接插值50%#8D817B141, 129, 12320, 0.07, 0.520.2279
sRGB 直接插值75%#C18F43193, 143, 6736, 0.50, 0.510.3139
sRGB 直接插值100%#F59E0B245, 158, 1138, 0.92, 0.500.4389
HSL 插值0%#2563EB37, 99, 235221, 0.83, 0.530.1532
HSL 插值25%#761FED118, 31, 237265, 0.85, 0.530.1095
HSL 插值50%#F018CE240, 24, 206309, 0.88, 0.520.2363
HSL 插值75%#F2122A242, 18, 42354, 0.90, 0.510.1948
HSL 插值100%#F59E0B245, 158, 1138, 0.92, 0.500.4389
OKLCH 插值0%#2563EB37, 99, 235221, 0.83, 0.530.1532
OKLCH 插值25%#9D57E0157, 87, 224271, 0.69, 0.610.1937

提示:在色值框、停止点数或角度框里按 Enter 立即重算;在页面上按 R 反向渐变、按 I 轮换插值空间。打开页面时起点 #2563EB、终点 #F59E0B、5 个停止点、90 度、OKLCH 插值,与下方「完整示例」和对照表是同一组参数。

使用方法

  1. 页面打开时已经是一组示例:起点 #2563EB、终点 #F59E0B、停止点数 5、方向角 90 度、插值空间 OKLCH、类型 linear-gradient。预览条、CSS 代码框、下面「完整示例」和对照表用的都是这一组参数。
  2. 改颜色两条路:拖拾色器,或在旁边的文本框里键入六位十六进制值(#2563EB 与 2563EB 都接受,三位缩写不接受)。起点与终点任意一处改动都会立即重算整条色带。
  3. 调停止点数(2~9 的整数):它决定色带上显式写出的色标个数,位置按 100 / (n − 1) 等距排布。5 个点就是 0%、25%、50%、75%、100%。
  4. 调方向角(0~360 的整数):只对 linear-gradient 生效。0 度指向正上方、90 度指向正右方、180 度向下、270 度向左,角度按顺时针增加。
  5. 选插值空间:sRGB 直接插值最接近很多浏览器里无提示的默认行为,HSL 走最短色相路径,OKLCH 在感知均匀的 Oklab 空间里插值。三个空间算出来的中间色差别很大,可以逐个切过去看。
  6. 选渐变类型:linear / radial / conic 三种,CSS 代码框会同步换成对应的函数写法。radial 与 conic 不使用方向角,角度框会被忽略,页面右下角的提示会说明这一点。
  7. 点「复制 CSS」把两行代码拿走(第一行是兜底纯色,第二行是渐变);「反向渐变」交换起点与终点;「随机配色」从色相环上随机取一对颜色;「载入示例」还原默认值。

渐变色生成器能解决什么问题

一是把「渐变中间发灰」这件事定位到具体色值。蓝色到橙黄是最常见的一对互补色,在 sRGB 里直接插值,50% 的位置会得到 #8D817B——一个饱和度只有 0.07 的脏灰色,视觉上像一块被洗掉的污渍。本工具把同一对颜色在 OKLCH 空间里插值,50% 的位置是 #DF57A9,饱和度保持在 0.55 上下,色带从头到尾都是一条干净的弧线。切换插值空间就能直接看到两条色带的差别。

二是给出可以直接粘进样式表的代码,而不是一张图。输出里除了 background-image 那行渐变,前面还有一行 background-color 纯色兜底——渐变渲染失败时至少还有一个品牌色,这是很多手写渐变的项目会漏掉的一步。

三是把中间色逐档列出来。设计师给的往往只有两端色,开发需要中间色时只能靠取色器在截图上一格一格吸。对照表把三种空间、每个停止点的 HEX、RGB、HSL、相对亮度全部列出来,要哪一档直接抄。

四是验证一套配色能不能当色阶用。做数据可视化或多层背景时,需要一条亮度递增的连续色带;把两端的明度拉开、切成 9 个停止点,看对照表里相对亮度那一列是否单调递增就够了,比肉眼判断可靠。

这个渐变色生成器不做这些事:不导出 PNG 图片(输出的是 CSS 代码而不是位图)、不做多段渐变(一条色带只有一对端点色)、也不做色盲与对比度检查——文字压在色带上能不能看清,请用页面底部的「颜色对比度检测」逐档验证。

原理和变量:sRGB、HSL、OKLCH 三种插值

所有渐变生成器都要回答同一个问题:两个端点色之间,第 t 档(t 从 0 到 1)取什么颜色?区别只在于「在哪个空间里做线性插值」。本页实现三种,并给出每一步的换算。

sRGB 直接插值。把两个端点拆成 R、G、B 三个 0~255 的数,逐通道按 c = c1 + (c2 − c1) × t 插值,再拼回去。这是最省算力的做法,也是很多浏览器在 linear-gradient 里实际使用的做法。它的问题不在于「不准确」,而在于 sRGB 是 gamma 编码空间——两个数值的中点并不等于视觉上的中点。示例这一对色在 50% 处得到 #8D817B,相对亮度 0.2279,比两端都出现了一个明显的灰化。

HSL 插值。先把端点转到 HSL,色相 H 走最短路径:当两个色相差的绝对值超过 180 度时,把方向反过来算(等价于 delta = ((h2 − h1 + 540) mod 360) − 180),否则从蓝 221 度一路顺时针走到橙 38 度要绕 183 度,会经过绿色;走反向的 176 度则经过品红。示例的色相差是 −183.5 度,所以工具选了反向路径,50% 落在 #F018CE。S 与 L 则各自线性插值。HSL 的 L 只是「最大最小通道的平均值」,不是感知亮度,所以它在明度上仍会跑偏。

OKLCH 插值。先把 sRGB 做 gamma 反变换得到线性 RGB(分界点取 0.04045,这是 IEC 61966-2-1 的现行写法),再用三组 LMS 矩阵和立方根变换到 Oklab,然后取成极坐标 L、C、H:L 是感知亮度,C 是色度(饱和度),H 是色相角。插值时 L 与 C 线性、H 走最短路径,最后逆变换回线性 RGB 并做 gamma 编码。Oklab 由 Björn Ottosson 在 2020 年提出,设计目标就是让这个空间里的欧氏距离尽量接近人眼感知的色差,所以它的「中点」看起来才真的是中间。渐变色生成器把 OKLCH 设为默认空间,原因就在这里:示例的 OKLCH 路径在 50% 给出 #DF57A9。

正反两个方向的矩阵都写在这里,方便你拿别的工具复核。sRGB 线性 → Oklab:l = 0.4122214708R + 0.5363325363G + 0.0514459929B,m = 0.2119034982R + 0.6806995451G + 0.1073969566B,s = 0.0883024619R + 0.2817188376G + 0.6299787005B,取三者的立方根得 l′、m′、s′,再 L = 0.2104542553l′ + 0.7936177850m′ − 0.0040720468s′、a = 1.9779984951l′ − 2.4285922050m′ + 0.4505937099s′、b = 0.0259040371l′ + 0.7827717662m′ − 0.8086757660s′。反向则是 l′ = L + 0.3963377774a + 0.2158037573b(m′、s′ 同理,系数分别是 −0.1055613458 / −0.0638541728 与 −0.0894841775 / −1.2914855480),立方回去后 R = 4.0767416621l − 3.3077115913m + 0.2309699292s、G = −1.2684380046l + 2.6097574011m − 0.3413193965s、B = −0.0041960863l − 0.7034186147m + 1.7076147010s。

停止点位置公式。第 i 个停止点(i 从 0 开始)的位置是 i / (n − 1) × 100%。n = 2 时只有 0% 与 100%;n = 5 时步长 25%;n = 9 时步长 12.5%,依次是 0%、12.50%、25%、37.50%、50%、62.50%、75%、87.50%、100%。CSS 里的百分比是沿渐变轴的投影位置,不是沿屏幕距离。

角度约定。CSS 的渐变角度以「向上」为 0 度、顺时针增加,所以 0deg 等价于 to top、90deg 等价于 to right、180deg 等价于 to bottom、270deg 等价于 to left。新手常把 90deg 当成「从左到右」——那是 to right 的含义没错,但它的角度值就是 90,而「从左上到右下」是 135deg,不是 45deg。

完整示例

示例参数与默认值一致:起点 #2563EB、终点 #F59E0B、5 个停止点、90 度、OKLCH 插值、linear-gradient。先把两端转成中间量,再列三个空间的实际取色。

三空间在 25% / 50% / 75% 三个位置的实际取色:

位置sRGB 直接插值HSL 插值OKLCH 插值
25%#5972B3(L 0.1741)#761FED(L 0.1095)#9D57E0(L 0.1937)
50%#8D817B(L 0.2279)#F018CE(L 0.2363)#DF57A9(L 0.2537)
75%#C18F43(L 0.3139)#F2122A(L 0.1948)#FE6F60(L 0.3328)

最该看的是 50% 那一列:sRGB 给出 #8D817B,HSL 饱和度只有 0.07,肉眼看接近一块脏灰;HSL 给出 #F018CE,是最短路径绕到品红的结果,起点到终点之间亮度先降后升(0.1532 → 0.1095 → 0.1948);OKLCH 给出 #DF57A9,相对亮度 0.2537,是三者里唯一在整条色带上保持亮度单调递增的(0.1532 → 0.1937 → 0.2537 → 0.3328 → 0.4389)。

于是默认输出的 CSS 是:

background-color: #2563EB; background-image: linear-gradient(90deg, #2563EB 0%, #9D57E0 25%, #DF57A9 50%, #FE6F60 75%, #F59E0B 100%);

把插值空间换成 sRGB,同一组参数变成 #2563EB 0%, #5972B3 25%, #8D817B 50%, #C18F43 75%, #F59E0B 100%;换成 HSL 则变成 #2563EB 0%, #761FED 25%, #F018CE 50%, #F2122A 75%, #F59E0B 100%。三条色带的端点完全相同,中间三个色标一个都不一样。

输入范围与对照表

本页接受的输入范围,以及越界时的处理:

下表把三种空间在「几对常见端点色」上的 50% 取色放在一起。这几对色的共同点是:sRGB 直连的中间都会明显偏离视觉预期。

端点对50% · sRGB50% · HSL50% · OKLCH
#0000FF → #FFAA00#805580#FF00AA#E728A0
#2563EB → #F59E0B#8D817B#F018CE#DF57A9
#FF0000 → #00FF00#808000#FFFF00#F99500
#000000 → #FFFFFF#808080#808080#636363

最后一行很能说明问题:黑白渐变在 sRGB 与 HSL 里中点都是 #808080,这是三个通道各取一半的结果;而 OKLCH 给出的中点是 #636363,明显更深。#636363 的 Oklab 亮度正好是 0.5,也就是这个空间里真正的感知中点,#808080 的 Oklab 亮度是 0.5999。做「黑到白」的进度条或骨架屏时,这个差别一眼可见——这类只能靠对照表才看得出来的差异,正是渐变色生成器把三个空间并排列出的意义。

再补一张停止点数与位置对照:

停止点数 n步长 100/(n−1)各停止点位置对照表行数 3n
2100%0%, 100%6
350%0%, 50%, 100%9
5(默认)25%0%, 25%, 50%, 75%, 100%15
912.5%0%, 12.5%, 25%, 37.5%, 50%, 62.5%, 75%, 87.5%, 100%27

结果边界与舍入误差

渐变色生成器在下面几个地方刻意做了取舍,知道边界在哪,色带才不会用错地方。

常见问题

为什么 sRGB 直连会让蓝色到橙色中间发灰?

因为 sRGB 把每个通道存成 gamma 编码值,数值上的「一半」并不是亮度上的一半。蓝色是 #2563EB(R 37、G 99、B 235),橙色是 #F59E0B(R 245、G 158、B 11),逐通道取中就是 R 141、G 129、B 123——三个分量被拉到了几乎相等的水平,于是变成灰色。正确做法是先线性化到光强、在感知均匀的空间里插值,再编码回来,也就是 OKLCH 那条路径。

OKLCH 和 OKLab 是一回事吗?

是同一个空间的两种写法。Oklab 用直角坐标(L、a、b)表达,OKLCH 把它转成极坐标(L、C、H),C 是色度、H 是色相角。做插值时用极坐标更顺手,因为可以单独控制「色相怎么走」和「饱和度怎么变」;做色差计算时用直角坐标更直接,欧氏距离就是色差。本页的 OKLCH 插值内部就是把两端转成极坐标、分别插值后再转回去。

「最短色相路径」是谁定的?我可以强制走另一边吗?

最短路径是 CSS Color 4 规范里对色相插值给出的默认行为(hue interpolation 的 default 关键字),指的是把色相差归一到 −180 到 180 之间。本页照此实现,所以示例里蓝到橙走了反向的 176.5 度而不是正向的 183.5 度。想强制走另一边,把起点和终点互换即可——反向走出来的路径就是原来的另一条。

0deg 到底朝哪?为什么我感觉方向反了?

CSS 的渐变角度以「向上」为 0 度,顺时针为正,所以 0deg 是指向上方(等价于 to top),色标从下往上排列;90deg 指向右方。很多人凭直觉以为 0 度是「从左到右」,于是把横向渐变写成了 0deg,结果拿到一条竖直渐变。记住 to <方向> 这种写法不需要记角度,不涉及动画时优先用它。

两个色标之间的颜色,浏览器算出来和我算的为什么对不上?

因为你在两个色标之间又插了一次值,而这次的插值由浏览器决定。本页给出的色值只在色标位置上精确成立;色标之间的那一段,浏览器可能在线性 RGB 里插,也可能在 gamma 编码的 sRGB 里插,不同引擎的历史行为并不统一。需要精确控制时,把停止点数提高到 9,让色标密到肉眼看不出中间段。

停止点数越多渐变越平滑吗?

不会更平滑,只是更精确。linear-gradient 本身在两个色标之间就是线性插值,多加色标等于把同一条折线画得更细;真正影响「顺不顺」的是插值空间与端点色的选择。把停止点数从 5 提到 9,唯一的变化是你在色带中段少了一点「不知道具体是什么色」的区域,以及配合色标位置做动画时能更精确地停住。

为什么输出里有一行 background-color?

那是兜底色。少数老旧引擎或某些打印场景不会渲染渐变,此时 background-image 整行被忽略,元素就会变成透明的;前面先声明一个 background-color 等于起始色,至少还能看到一个统一的品牌色。这不是过时的兼容写法,而是渐变背景的常规防御措施,成本只有一行代码。

渐变的中点颜色和「混合两个颜色」是一回事吗?

不是。混色工具(例如 sRGB 的 alpha 混合)算的是两个颜色按比例叠加后的实际结果,参与运算的是带 alpha 的分量;而渐变插值算的是把 t 从 0 推到 1 时色带上的取样点。两者在 sRGB 里逐通道计算时结果常常一致,但一旦进入 HSL 或 OKLCH,或者两端带透明度,就会分道扬镳。要算叠加结果请用「调色工具」,要做色带请用本页。

相关工具