用代码着色,一种程序化的设计绝佳方法。学习创建美丽、鼓舞人心和独特的调色板/组合,所有都会让你的网页和文本编辑变得更加舒适!废话不多说,今天带着大家认识下LCH颜色表达方式!
颜色是强大的——它可以从根本上改变我们的情绪,激励我们,并帮助我们以一种其他事物无法做到的方式表达自己。在设计中我们常听到的是RGB(常用的网页颜色三元素)以及打印的CMYK;为了更好的视觉效果,我们也需要了解LCH颜色!这样可以让我们的网页更加有层次感,高级感十足!当你了解了这篇文章后,绝对会提升你的审美观。老板看了你写的网页,绝对是赞不绝口。还要给你加薪!
什么是LCH颜色
LCH 代表亮度 (颜色的深/浅)、色度 (颜色的鲜艳/饱和度)和色调 (颜色是红色、绿色、蓝色……)。
简而言之,LCH 是一种与 RGB 或 HSL 一样的颜色表示方式,但有一些显着的优势——对于本教程来说最重要的是它的感知均匀性。在本教程中,我们几乎只使用 LCH 颜色。我知道这听起来有点吓人,但我保证不会;让我告诉你这意味着什么!
首先,看看这两对 HSL 颜色:
请注意,尽管顶部和底部对具有相同的 20 度色相变化,但我们 实际 看到的差异却大不相同?这种不平衡的存在是因为 HSL 在感知上 并不 统一。
现在看看同样的实验,这次使用 LCH 颜色:
HSL 和 LCH 之间的色调值不能完美对齐。在 LCH 中,0 的色调更粉红色,而在 HSL 中,它是纯红色。 啊,好多了!
这里看到的色调变化更加平衡,因为 LCH 在 感知上是均匀的。 接下来,我们来看看另外两种 HSL 颜色:
这两种颜色具有相同的亮度值,但在我们的人眼看来却大不相同。左边的黄色比右边的蓝色“亮”得多。
这是一个类似的设置,但使用的是 LCH 颜色而不是 HSL:
这还差不多!如上图所示,LCH 中的亮度值更准确地表示了我们所感知的内容——这与 LCH 的均匀色调分布相结合,将使我们 在创建和谐的调色板时更加轻松 。
目前,这就是我们需要知道的全部内容,但如果您想了解更多信息,我强烈推荐Lea Verou 撰写的这篇文章。
我们将在本教程中使用一个库,但本机 LCH 支持正在走向浏览器!事实上,它已经存在于 Safari 中,其他浏览器目前也在使用它。
跟着
在我们编写任何代码之前,我们需要一个简单的开发环境。此设置完全由您选择,但我建议您启动 CodePen 或者vscode以跟随示例,然后在需要时移至自定义设置/存储库。真的,我们在这里只需要一个 HTML/JavaScript 文件,我们将使用 Skypack 进行所有库导入,因此不需要任何花哨的构建过程等。
功能#1——“科学”
好的!首先,我们使用“传统”颜色理论生成颜色。要开始使用此方法,让我们看一下称为色轮的东西:
看起来熟悉?
色轮是色彩空间中色调的视觉表示。上面的轮子代表 LCH 中的色调,以 30 度的步长递增,从 0 度到 360 度——这是一种成熟的格式。事实上,数百年来,我们一直在使用轮子来寻找可以很好地协同工作的颜色!
这是如何做:
我们从基色开始。然后,我们围绕轮子旋转一定度数一定次数;为了完美 互补的 调色板,我们移动 180 度一次:
迷人的!对于 三元 调色板,我们移动 120 度,两次:
看看这是怎么回事?通过改变步数和旋转量,我们可以创建几个“经典”调色板:
代码
function adjustHue(val) { if (val < 0) val += Math.ceil(-val / 360) * 360; return val % 360; } function createScientificPalettes(baseColor) { const targetHueSteps = { analogous: [0, 30, 60], triadic: [0, 120, 240], tetradic: [0, 90, 180, 270], complementary: [0, 180], splitComplementary: [0, 150, 210] }; const palettes = {}; for (const type of Object.keys(targetHueSteps)) { palettes[type] = targetHueSteps[type].map((step) => ({ l: baseColor.l, c: baseColor.c, h: adjustHue(baseColor.h + step), mode: "lch" })); } return palettes; }
要打破这一点:
-
定义一个createScientificPalettes需要单个baseColor参数的函数。
-
定义几个“经典”调色板的色调步骤。
-
对于每种调色板类型:迭代每个色调步长,将步长值添加到基本色调,并存储生成的颜色——确保其chroma和lightness值与基本色调匹配。使用一个小adjustHue函数来确保所有色调值都在 0 到 360 之间。
-
以 LCH 格式返回调色板。
惊人的!我们可以createScientificPalettes
这样调用我们的函数:
const baseColor = {
l: 50,
c: 100,
h: 0,
mode: "lch"
};
const palettes = createScientificPalettes(baseColor);
在上面的示例中,我们传递一个baseColor
对象,该函数返回各种调色板,所有这些调色板都以该基础为中心。多亏了 LCH,这些调色板中颜色的亮度和强度将在视觉上保持一致,并且色调调制高度准确;这对于可访问性非常有用,因为与其他颜色空间不同,调色板中的每种颜色都将具有相同的 感知 对比度。
凉爽的!现在剩下要做的就是将 LCH 颜色转换为更可用的格式。为此,我们可以使用Culori(本教程中使用的一个出色的颜色实用程序库)将 LCH 对象转换为 HEX:
import { formatHex } from "https://cdn.skypack.dev/culori@2.0.0";
const baseColor = {
l: 50,
c: 100,
h: 0,
mode: "lch"
};
const palettes = createScientificPalettes(baseColor);
const triadicHex = palettes.triadic.map((colorLCH) => formatHex(colorLCH));
// ["#ff007c", "#1f8a00", "#0091ff"]
Culori 需要mode
对所有颜色对象进行显式处理。您会注意到这包含在本教程的代码示例中。
对于我们的第一个函数,就是这样!让我们看看我们如何在现实生活中使用它。
实际应用使用代码(以编程方式) 创建我们的调色板的一个好处 是它使快速原型设计/实验变得非常容易。比如说,我们正在做一个设计,完全不知道要使用什么调色板。使用我们的createScientificPalettes函数以及一些简单的 CSS 自定义属性,我们可以生成近乎无限的调色板并使用我们的 UI 实时测试它们!
这是一个 CodePen 来演示:
挑战
现在,我们的createScientificPalettes函数适用于所有调色板类型,除了 单色。您可以更新它以支持单色调色板吗?
功能#2——“发现”
因此,此功能与前一个功能相似,但有 很大 的不同。我们仍在生成“经典”颜色组合,但不是科学地计算它们 (将设置“步骤”添加到基色的色调中), 我们正在 发现 它们!这是正确的; 我们的发现功能将采用一系列颜色并在其中找到最佳的调色板匹配——类似的、三色的、四色的等。
这是一个图解示例:
使用此功能,我们可以在图像、颜色数据集等中发现漂亮的调色板!让我们看看它是如何工作的。
代码
import { nearest, differenceEuclidean, } from "https://cdn.skypack.dev/culori@2.0.0"; function isColorEqual(c1, c2) { return c1.h === c2.h && c1.l === c2.l && c1.c === c2.c; } function discoverPalettes(colors) { const palettes = {}; for (const color of colors) { const targetPalettes = createScientificPalettes(color); for (const paletteType of Object.keys(targetPalettes)) { const palette = []; let variance = 0; for (const targetColor of targetPalettes[paletteType]) { // filter out colors already in the palette const availableColors = colors.filter( (color1) => !palette.some((color2) => isColorEqual(color1, color2)) ); const match = nearest( availableColors, differenceEuclidean("lch") )(targetColor)[0]; variance += differenceEuclidean("lch")(targetColor, match); palette.push(match); } if (!palettes[paletteType] || variance < palettes[paletteType].variance) { palettes[paletteType] = { colors: palette, variance }; } } } return palettes; }
-
将 LCH 颜色数组传递给
discoverPalettes
函数。 -
createScientificPalettes
对于每种颜色,使用我们的函数 基于它创建“最佳”目标调色板。 -
对于每个调色板,为其每种颜色找到最接近的匹配。我们在这里使用 Culori 的最接近和差分欧几里得函数计算颜色匹配。
-
确定“发现”调色板与目标的相似/不同程度。记录最接近的调色板匹配。
-
返回每个调色板类型最接近的匹配!
惊人的!这种方法非常令人兴奋,因为它的操作方式与人类非常相似——查看选择的颜色并找到最好的(但绝不是 完美的)调色板;这很棒,因为有时,纯粹的数学色彩理论可能会显得有些枯燥/可预测。
用法
作为快速参考,以下是我们如何使用discoverPalettes十六进制颜色数组:
import {
converter,
} from "https://cdn.skypack.dev/culori@2.0.0";
const toLCH = converter("lch");
const baseColors = [
"#FFB97A",
"#FF957C",
"#FF727F",
"#FF5083",
"#F02F87",
"#C70084",
"#9A007F",
"#6A0076",
"#33006B"
];
const baseColorsLCH = baseColors.map((color) => toLCH(color));
const palettes = discoverPalettes(baseColorsLCH);
// { analogous: [...], complementary: [...], ... }
至少需要四种颜色才能正常工作。 discoverPalettes
最引人注目的方面之一discoverPalettes是它能够从几乎任何来源中提取连贯的颜色组合。就是这样,根据Unsplash中的图像发现调色板:
酷吧?从照片中提取调色板是一种奇妙的工作方式,当您遇到想法时,并且discoverPalettes使过程 非常容易。这种方法以前只能通过“神奇的”颜色生成器/应用程序获得,现在就在我们的指尖,可以进行调整、迭代和改进,以适应我们自己的个人用例和偏好!
挑战现在,我们的discoverPalettes函数可以在一组颜色中找到最佳匹配,但控制起来并不容易。您可以为其选择添加一定程度的偏差/权重吗?例如,您如何修改函数以优先考虑更亮的颜色?
功能#3——“色相转换”
对于我们的第三个也是最后一个功能,我们将从像素艺术世界中汲取灵感!
通常在向精灵添加阴影/高光时,像素艺术家不仅会调制颜色的亮度/色度(如果使用 HSL,则为饱和度),还会改变其色调。这是一个关于这个主题的优秀视频,但简而言之,这就是它的样子:
真好看!当颜色变浅时,它的色调就会上移;当它变暗时,它会向下移动。当巧妙地应用时,这种技术有助于确保颜色的色调/色调生动而有影响力。当“拨号”一点时,它是生成令人惊叹的独立调色板的绝妙方式。
代码function adjustHue(val) { if (val < 0) val += Math.ceil(-val / 360) * 360; return val % 360; } function map(n, start1, end1, start2, end2) { return ((n - start1) / (end1 - start1)) * (end2 - start2) + start2; } function createHueShiftPalette(opts) { const { base, minLightness, maxLightness, hueStep } = opts; const palette = [base]; for (let i = 1; i < 5; i++) { const hueDark = adjustHue(base.h - hueStep * i); const hueLight = adjustHue(base.h + hueStep * i); const lightnessDark = map(i, 0, 4, base.l, minLightness); const lightnessLight = map(i, 0, 4, base.l, maxLightness); const chroma = base.c; palette.push({ l: lightnessDark, c: chroma, h: hueDark, mode: "lch" }); palette.unshift({ l: lightnessLight, c: chroma, h: hueLight, mode: "lch" }); } return palette; }
将其分解为以下步骤:
-
将基色、最小/最大亮度和色调步长参数传递给createHueShiftPalette函数。最小/最大亮度值决定了我们的调色板在任一极端时有多暗/多亮。该步骤控制每种颜色的色调变化量。
-
将基色存储在数组中。在上图中,这是中间颜色。
-
创建一个迭代四次的循环。每次迭代,在数组的开头添加较深的阴影,在末尾添加较浅的色调。在这里,我们map用来计算亮度值——一个函数,它接受一个通常存在于一个范围内的数字并将其转换为另一个范围——并使用我们的hueStep变量增加或减少色调。同样,adjustHue这里使用 来确保所有色调值都在 0 到 360 之间。
-
归还调色板!
一旦我们的createHueShiftPalette函数被定义,我们可以像这样使用它:
import { formatHex } from "https://cdn.skypack.dev/culori@2.0.0";
const hueShiftPalette = createHueShiftPalette({
base: {
l: 55,
c: 75,
h: 0,
mode: "lch"
},
minLightness: 10,
maxLightness: 90,
hueStep: 12
});
const hueShiftPaletteHex = hueShiftPalette.map((color) => formatHex(color));
// ["#ffb97a", "#ff957c", "#ff727f", "#ff5083", "#f02f87", "#c70084", "#9a007f", "#6a0076", "#33006b"]
实际应用
生成的调色板createHueShiftPalette非常 适合 图案/图形;这是一个使用它来创建随机/生成模式的示例,这些模式在每次渲染时都会略有不同:
挑战
现在,亮度/色调值在我们的createHueShiftPalette函数中线性缩放。你可以对它们应用一些缓和吗?也许,从更大/更小的色调偏移开始,并随着每一步减少/增加它?好吧,伙计们,这就是现在的全部!我们已经学习了如何创建三个漂亮的颜色生成函数,了解如何应用它们并考虑如何改进/更改它们。从这里开始,我希望您采用这些功能并更改它们以适合您,甚至希望您自己编写!作为开发人员,我们拥有独特的技能组合, 非常 适合创建真正创新、令人惊叹的设计。无论这意味着为与您合作的设计师创建颜色生成工具,还是为您的网站添加令人兴奋的生成调色板——我们都应该对我们使用颜色的能力充满信心。
如有相关前端方面的技术问题 ,欢迎主页添加我们微信群,我会定期在群里给大家分享最新技术和解答问题 。