Project Icon

color

现代JavaScript颜色处理库 高效精准轻量级

@texel/color是一个现代JavaScript颜色处理库,为实时应用、生成艺术和网页图形提供高效解决方案。该库支持快速颜色转换、色差计算、色域映射和序列化,在速度和精确度方面表现出色。支持OKLCH、DisplayP3等现代颜色空间,并可通过tree-shaking优化包体积。相比其他类似库,性能提升5-125倍,内存占用更低。

@texel/color

生成的图片

这是一个简洁现代的JavaScript颜色库。特别适用于实时应用、生成艺术和网页图形。

  • 功能:快速颜色转换、色差计算、色域映射和序列化
  • 速度优化:比Colorjs.io快约5-125倍(参见基准测试
  • 内存和分配优化:转换和色域映射函数内不创建数组或对象
  • 优化打包体积:零依赖,未使用的色彩空间可自动被树摇除去,保持小体积(例如,如果只需要OKLCH到sRGB的转换,压缩后约3.5kb)
  • 精确度优化:高精度色彩空间矩阵
  • 专注于最小化和现代化的色彩空间集合:
    • xyz (D65)、xyz-d50、oklab、oklch、okhsv、okhsl、srgb、srgb-linear、display-p3、display-p3-linear、rec2020、rec2020-linear、a98-rgb、a98-rgb-linear、prophoto-rgb、prophoto-rgb-linear

安装

使用npm安装并导入模块。

npm install @texel/color --save

示例

将OKLCH(OKLab的圆柱形式)转换为sRGB:

import { convert, OKLCH, sRGB } from "@texel/color";

// L = 0 .. 1
// C = 0 .. 0.4
// H = 0 .. 360(度)
const rgb = convert([0.5, 0.15, 30], OKLCH, sRGB);

// 注意sRGB输出范围为0 .. 1
// -> [ 0.658, 0.217, 0.165 ]

你也可以使用通配符导入:

import * as colors from "@texel/color";

const rgb = colors.convert([0.5, 0.15, 30], colors.OKLCH, colors.sRGB);

:bulb: 现代打包工具(esbuild、vite)会应用树摇,移除未使用的功能,如代码中未引用的色彩空间和色域映射函数。上述脚本使用esbuild打包后约3.5kb。

另一个使用色域映射和序列化的宽色域Canvas2D示例:

import { gamutMapOKLCH, DisplayP3Gamut, sRGBGamut, serialize } from "@texel/color";

// 可能在或不在sRGB色域内的值
const oklch = [ 0.15, 0.425, 30 ];

// 决定要映射到哪个色域
const isDisplayP3Supported = /* 检查环境 */;
const gamut = isDisplayP3Supported ? DisplayP3Gamut : sRGBGamut;

// 将输入的OKLCH映射到R,G,B空间(sRGB或DisplayP3)
const rgb = gamutMapOKLCH(oklch, gamut);

// 获取输出空间的CSS颜色字符串
const color = serialize(rgb, gamut.space);

// 在Canvas2D上下文中绘制颜色
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d', {
  colorSpace: gamut.id
});
context.fillStyle = color;
context.fillRect(0,0, canvas.width, canvas.height);

API

output = convert(coords, fromSpace, toSpace, output = [0, 0, 0])

coords(通常为[r,g,b][l,c,h]等)从fromSpace转换到指定的toSpace。from和to空间是spaces接口之一。

你可以传入output,一个三维向量,结果将存储在其中。这可用于避免在热代码路径中分配新内存。

返回值是目标空间的新坐标;如sRGB空间的目标为[r,g,b]。注意,大多数空间使用归一化且无界的坐标;所以RGB空间的范围是0..1,可能会超出范围(即超出色域)。你可能需要结合gamutMapOKLCH使用,见下文。

output = gamutMapOKLCH(oklch, gamut = sRGBGamut, targetSpace = gamut.space, out = [0, 0, 0], mapping = MapToCuspL, [cusp])

执行Björn Ottoson描述的(2021)快速OKLCH色域映射。接受OKLCH空间的输入[l,c,h]坐标,确保最终结果在指定的颜色gamut内(默认sRGBGamut)。你可以进一步指定不同的目标空间(默认为色域的空间),例如获取线性光sRGB并避免传递函数,或保持结果在OKLCH中:

import { gamutMapOKLCH, sRGBGamut, sRGBLinear, OKLCH } from "@texel/color";

// 映射到sRGB但返回线性sRGB
const lrgb = gamutMapOKLCH(oklch, sRGBGamut, sRGBLinear);

// 或映射到sRGB但返回OKLCH(不执行RGB裁剪)
const lch = gamutMapOKLCH(oklch, sRGBGamut, OKLCH);

你可以指定out数组以避免分配,结果将存储在该数组中。你还可以指定mapping函数,决定色域映射时使用的策略,可以是以下之一:

import {
  // 可能的映射
  MapToL,
  MapToGray,
  MapToCuspL,
  MapToAdaptiveGray,
  MapToAdaptiveCuspL,
} from "@texel/color";

// 执行sRGB色域映射时保持亮度
const rgb = [0, 0, 0];
gamutMapOKLCH(oklch, sRGBGamut, sRGB, rgb, MapToL);

cusp也可作为最后一个参数传入,允许对已知色相进行更快的评估。见下文计算cusp。

注意: 如果映射到基于OKLab的目标(OKLCH、OKHSL等),将跳过最后的RGB裁剪步骤。这会产生更可预测的OKLab和OKLCH结果,但在转换为可显示颜色时,你可能需要执行最后的clampedRGB()步骤。

LC = findCuspOKLCH(a, b, gamut, out = [0, 0])

找到给定OKLab色相平面(用OKLab空间中的归一化ab值表示)的'cusp',返回[L, C](亮度和色度)。当你在已知色相上工作时,这对预计算色域映射的某些方面很有用:

import {
  sRGBGamut,
  findCuspOKLCH,
  gamutMapOKLCH,
  degToRad,
  MapToCuspL,
} from "@texel/color";

const gamut = sRGBGamut;

// 为这个色相计算一次cusp
const H = 30; // 例如30º色相
const hueAngle = degToRad(H);
const a = Math.cos(hueAngle);
const b = Math.sin(hueAngle);
const cuspLC = findCuspOKLCH(a, b, gamut);

// ... 在程序的其他地方 ...
// 传入'cusp'参数以加快评估
// 假设你的OKLCH坐标与cusp有相同的色相(H)
gamutMapOKLCH(oklch, gamut, gamut.space, out, MapToCuspL, cuspLC);

ab也可以来自OKLab坐标,但必须归一化使a^2 + b^2 == 1

str = serialize(coords, inputSpace, outputSpace = inputSpace)

将指定的coords(假定在inputSpace中)转换为字符串,如果需要,先转换到指定的outputSpace。如果空间是sRGB,为了浏览器兼容性和性能,将使用普通的rgb(r,g,b)字符串(字节形式),否则将返回CSS颜色字符串。注意,并非所有空间(如某些线性空间)目前都受CSS支持。你可以选择在coords数组中传入第四个元素作为alpha分量(0..1范围)。

import { serialize, sRGB, DisplayP3, OKLCH } from "@texel/color";
serialize([0, 0.5, 1], sRGB); // "rgb(0, 128, 255)"
serialize([0, 0.5, 1, 0.5], sRGB); // "rgba(0, 128, 255, 0.5)"
serialize([0, 0.5, 1], DisplayP3); // "color(display-p3 0 0.5 1)"
serialize([0, 0.5, 1, 0.35], DisplayP3); // "color(display-p3 0 0.5 1 / 0.35)"
serialize([1, 0, 0], OKLCH, sRGB); // "rgb(255, 255, 255)"
serialize([1, 0, 0], OKLCH); // "oklch(1 0 0)"

info = deserialize(colorString)

这是serialize的反向操作,它会接收一个字符串,确定它引用的色彩空间id,以及3个或4个(含透明度)coords。这个功能有意地限制了范围,只支持十六进制RGB、rgb()rgba()字节值,以及oklch()oklab()和不带修饰符的普通color()函数。

import { deserialize } from "@texel/color";

const { coords, id } = deserialize("color(display-p3 0 0.5 1 / 0.35)");
console.log(id); // "display-p3"
console.log(coords); // [ 0, 0.5, 1, 0.35 ]

注意: 解析仍然是API设计中正在进行的工作,复杂的CSS颜色字符串处理不在本库的范围内。

delta = deltaEOK(oklabA, oklabB)

在OKLab空间中计算两个坐标之间的颜色差异。由于这是一个感知均匀的色彩空间,改进了CIELAB及其缺陷,因此在许多情况下它应该可以适当地替代CIEDE2000颜色差异方程。

[utils]

该模块还导出了许多其他实用工具

色彩空间

该模块导出了一组色彩空间:

import {
  XYZ, // 使用D65白点
  XYZD50, // 使用D50白点
  sRGB,
  sRGBLinear,
  DisplayP3,
  DisplayP3Linear,
  Rec2020,
  Rec2020Linear,
  A98RGB, // Adobe® 1998 RGB
  A98RGBLinear,
  ProPhotoRGB,
  ProPhotoRGBLinear,
  OKLab,
  OKLCH,
  OKHSL, // 在sRGB色域内
  OKHSV, // 在sRGB色域内

  // 列出所有空间的函数
  listColorSpaces,
} from "@texel/color";

console.log(listColorSpaces()); // [XYZ, sRGB, sRGBLinear, ...]

console.log(sRGBLinear.id); // "srgb-linear"
console.log(sRGB.base); // -> sRGBLinear
console.log(sRGB.fromBase(someLinearRGB)); // -> [gamma编码的sRGB...]
console.log(sRGB.toBase(someGammaRGB)); // -> [线性sRGB...]

注意,并非所有空间都有base字段;如果未指定,则假定色彩空间可以通过OKLab或XYZ作为根进行传递。

色域

该模块导出了一组"色域",这些色域是由OKLab空间中的近似值定义的边界,允许快速进行色域映射。这些接口主要由gamutMapOKLCH函数使用。

import {
  sRGBGamut,
  DisplayP3Gamut,
  Rec2020Gamut,
  A98RGBGamut,

  // 列出所有色域的函数
  listColorGamuts,
} from "@texel/color";

console.log(listColorGamuts()); // [sRGBGamut, ...]

console.log(sRGBGamut.space); // sRGB空间
console.log(sRGBGamut.space.id); // 'srgb'

注意:目前尚不支持ProPhoto色域,我欢迎通过Python脚本修复它的PR。

实用工具

除了核心API外,该模块还导出了许多实用工具:

b = floatToByte(f)

将0..1范围内的浮点数转换为0..255范围内的字节,进行四舍五入并限制在范围内。

out = XYZ_to_xyY(xyz, out=[0,0,0])

将XYZ坐标转换为xyY形式,如果指定了out,则将结果存储在其中后返回。

out = xyY_to_XYZ(xyY, out=[0,0,0])

将xyY坐标转换为XYZ形式,如果指定了out,则将结果存储在其中后返回。

v = lerp(min, max, t)

使用因子t在min和max之间执行线性插值。

v = lerpAngle(min, max, t)

使用因子t在min和max之间执行圆形线性插值,其中min和max被视为角度(以度为单位),允许值在0到360之间环绕,插值以创建最短的弧。

c = clamp(value, min, max)

value限制在min和max之间,并返回结果。

out = clampedRGB(inRGB, out=[0,0,0])

将RGB限制(即裁剪)在0..1范围内,如果指定了out,则将结果存储在其中后返回。

inside = isRGBInGamut(rgb, epsilon = 0.000075)

如果给定的rgb在其0..1色域边界内,则返回true,阈值为epsilon

rgb = hexToRGB(hex, out=[0,0,0])

将指定的十六进制字符串(带或不带前导#)转换为0..1范围内的浮点RGB三元组,如果指定了out,则将结果存储在其中后返回。

hex = RGBToHex(rgb)

将指定的RGB三元组(0..1范围内的浮点数)转换为带前导#的6字符十六进制颜色字符串。

angle = constrainAngle(angle)

angle(以度为单位)限制在0..360范围内,必要时进行环绕。

degAngle = radToDeg(radAngle)

将角度(以弧度为单位)转换为度。

radAngle = degToRad(degAngle)

将角度(以度为单位)转换为弧度。

变换矩阵

您还可以导入更低级别的函数和矩阵;这可能对细粒度转换有用,或者例如将缓冲区上传到WebGPU以进行计算着色器。

import {
  OKLab_to,
  OKLab_from,
  transform,
  XYZ_to_linear_sRGB_M,
  LMS_to_XYZ_M,
  XYZ_to_LMS_M,
  sRGB,
  OKHSLToOKLab,
  DisplayP3Gamut,
} from "@texel/color";

console.log(XYZ_to_linear_sRGB_M); // [ [a,b,c], ... ]
OKLab_to(oklab, LMS_to_XYZ_M); // OKLab -> XYZ D65
OKLab_from(xyzD65, XYZ_to_LMS_M); // XYZ D65 -> OKLab
transform(xyzD65, XYZ_to_linear_sRGB_M); // XYZ D65 -> sRGBLinear
sRGB.fromBase(in_linear_sRGB, out_sRGB); // 线性到gamma传递函数
sRGB.toBase(in_sRGB, out_linear_sRGB); // gamma到线性传递函数

// 非sRGB色域中的OKHSL
// 另见OKHSVToOKLab及其反函数
OKHSLToOKLab([h, s, l], DisplayP3Gamut, optionalOutVec);

插值

该库目前只公开了{ lerp, lerpAngle }函数。要插值颜色,您需要构建一些额外的逻辑,例如,请参见example-interpolation.js脚本,该脚本在Canvas2D中创建颜色渐变。

自定义色彩空间

您可以构建自定义色彩空间对象来扩展这个库,例如添加对CIELab和HSL的支持。有关示例,请参见test/spaces/lab.jstest/spaces/hsl.js。其中一些空间可能在以后添加到库中,尽管当前重点是"现代"空间(如OKLab,它在很大程度上使CIELab和HSL过时)。自定义色彩空间的文档正在编写中。

注意事项

为什么要开发另一个库?

Colorjs非常出色,可能是JavaScript中当前领先的标准,但它对于创意编码和实时Web应用程序来说并不是很实用,在这些应用程序中,要求通常是(1)更精简的代码库,(2)高度优化,以及(3)最小化GC抖动。

Colorjs,以及类似的Culori,专注于匹配CSS规范,这意味着它很可能会随着时间的推移继续增加复杂性,性能通常会受到影响(例如,@texel/color的尖点交叉色域映射比Colorjs快约125倍,比culori快约60倍)。 还有许多其他选项,如 color-spacecolor-convert,但这些不支持现代色彩空间,如 OKLab 和 OKHSL,和/或具有可疑的准确度(例如,许多库不区分 XYZ 中的 D50 和 D65 白点)。

支持的色彩空间

本库并不旨在涵盖所有色彩空间;它只专注于有限的"现代"集合,即 OKLab、OKHSL 和 DeltaEOK 在许多实际用途中已经取代了 CIELab、HSL 和 CIEDE2000,使得本库更简单、更精简。请注意,其他空间如 CIELab 和 HSL 可通过"自定义色彩空间"得到支持。

改进与技术

该模块使用了以下一些做法,以显著优化性能并减小打包体积:

  • 循环、闭包、解构和其他语法糖被替换为更优化的代码路径和简单的数组访问。
  • 移除了热点代码路径中的内存分配,必要时重复使用临时数组。
  • 某些转换,如 OKLab 到 sRGB,不需要先通过 XYZ,可以使用已知矩阵直接转换。
  • API 设计的结构使得色彩空间通常不在内部引用,允许它们自动进行树摇。

准确度

所有转换都经过测试,与 Colorjs 转换近似相等,误差在 2-33(10 位小数)以内,在某些情况下甚至更准确。

本库使用 coloraide 及其 Python 工具计算转换矩阵和 OKLab 色域近似值。一些矩阵已硬编码到脚本中,并尽可能使用有理数(如 CSS 颜色模块工作草案规范建议 的那样)。

如果您认为矩阵或准确度可以改进,请提交 PR。

基准测试

test 目录中有几个基准测试:

以下结果基于 MacBook Air M2。请注意,Colorjs 的性能取决于您使用的 API(默认的基于类的 API 比过程式 API 慢得多)。

与 Colorjs.io 的基准对比
转换(Colorjs.io 过程式 API)--
Colorjs.io:2955.88 毫秒
我们的:457.86 毫秒
加速:快 6.5 倍

转换(Colorjs.io 主要 API)--
Colorjs.io:10034.38 毫秒
我们的:452.11 毫秒
加速:快 22.2 倍

OKLCH - sRGB 色域映射(Colorjs.io 过程式 API)--
Colorjs.io:5602.46 毫秒
我们的:49.10 毫秒
加速:快 114.1 倍

OKLCH - sRGB 色域映射(Colorjs.io 主要 API)--
Colorjs.io:5913.80 毫秒
我们的:44.91 毫秒
加速:快 131.7 倍

所有空间到 P3 的色域映射(Colorjs.io 过程式 API)--
Colorjs.io:4693.43 毫秒
我们的:150.16 毫秒
加速:快 31.3 倍

所有空间到 P3 的色域映射(Colorjs.io 主要 API)--
Colorjs.io:5478.16 毫秒
我们的:145.88 毫秒
加速:快 37.6 倍
与 Culori 的基准对比
使用输入类型:OKLab L 平面的随机采样进行测试
OKLCH 到 P3 的转换 --
Culori:43.30 毫秒
我们的:12.83 毫秒
加速:快 3.4 倍

OKLCH 到 P3 色域的色域映射 --
Culori:1588.62 毫秒
我们的:23.05 毫秒
加速:快 68.9 倍

本地运行

克隆,npm install,然后运行 npm run 列出可用的脚本,或运行 npm t 执行测试。

致谢

这个库的实现得益于许多开发者和工程师的出色前期工作:

许可

MIT 许可,详见 LICENSE.md

项目侧边栏1项目侧边栏2
推荐项目
Project Cover

豆包MarsCode

豆包 MarsCode 是一款革命性的编程助手,通过AI技术提供代码补全、单测生成、代码解释和智能问答等功能,支持100+编程语言,与主流编辑器无缝集成,显著提升开发效率和代码质量。

Project Cover

AI写歌

Suno AI是一个革命性的AI音乐创作平台,能在短短30秒内帮助用户创作出一首完整的歌曲。无论是寻找创作灵感还是需要快速制作音乐,Suno AI都是音乐爱好者和专业人士的理想选择。

Project Cover

有言AI

有言平台提供一站式AIGC视频创作解决方案,通过智能技术简化视频制作流程。无论是企业宣传还是个人分享,有言都能帮助用户快速、轻松地制作出专业级别的视频内容。

Project Cover

Kimi

Kimi AI助手提供多语言对话支持,能够阅读和理解用户上传的文件内容,解析网页信息,并结合搜索结果为用户提供详尽的答案。无论是日常咨询还是专业问题,Kimi都能以友好、专业的方式提供帮助。

Project Cover

阿里绘蛙

绘蛙是阿里巴巴集团推出的革命性AI电商营销平台。利用尖端人工智能技术,为商家提供一键生成商品图和营销文案的服务,显著提升内容创作效率和营销效果。适用于淘宝、天猫等电商平台,让商品第一时间被种草。

Project Cover

吐司

探索Tensor.Art平台的独特AI模型,免费访问各种图像生成与AI训练工具,从Stable Diffusion等基础模型开始,轻松实现创新图像生成。体验前沿的AI技术,推动个人和企业的创新发展。

Project Cover

SubCat字幕猫

SubCat字幕猫APP是一款创新的视频播放器,它将改变您观看视频的方式!SubCat结合了先进的人工智能技术,为您提供即时视频字幕翻译,无论是本地视频还是网络流媒体,让您轻松享受各种语言的内容。

Project Cover

美间AI

美间AI创意设计平台,利用前沿AI技术,为设计师和营销人员提供一站式设计解决方案。从智能海报到3D效果图,再到文案生成,美间让创意设计更简单、更高效。

Project Cover

AIWritePaper论文写作

AIWritePaper论文写作是一站式AI论文写作辅助工具,简化了选题、文献检索至论文撰写的整个过程。通过简单设定,平台可快速生成高质量论文大纲和全文,配合图表、参考文献等一应俱全,同时提供开题报告和答辩PPT等增值服务,保障数据安全,有效提升写作效率和论文质量。

投诉举报邮箱: service@vectorlightyear.com
@2024 懂AI·鲁ICP备2024100362号-6·鲁公网安备37021002001498号