我最近用一个新主题和一组先锋角色刷新了我网站上的动画图形,将我在本系列中分享的大量技术付诸实践。当有人与我的一些动画互动或在一天中的不同时间时,我的一些动画会改变外观。

我的博客页面顶部图形的颜色每天从早到晚都会变化。然后是雪模式,它通过覆盖层和混合模式添加寒冷的颜色和冬季主题。

在研究这个问题时,我开始想知道 CSS 相对颜色值是否可以给我更多的控制权,同时也简化过程。 注意:在本教程中,我将重点介绍用于主题图形和动画的相对颜色值和 OKLCH 颜色空间。如果您想深入了解相对颜色,Ahmad Shadeed 创建了一个出色的交互式指南。至于色彩空间、色域和 OKLCH,我们自己的 Geoff Graham 写过相关内容。

重复使用元素是关键。尽可能重复使用背景,通过缩放和叠加帮助从同一艺术品构建新场景。它是出于必要而诞生的,但它也鼓励从系列而不是单个场景的角度进行思考。 手动更新调色板的问题 让我们直接面对我的挑战。在像这样的卡通标题中(基于 1959 年 Yogi Bear Show 剧集“Lullabye-Bye Bear”)以及我的一般作品,调色板仅限于精选的几种颜色。

我用我所谓的“基础”颜色创建阴影和色调,以扩展调色板而不添加更多色调。

在 Sketch 中,我在 HSL 色彩空间中工作,因此此过程涉及增加或减少基础颜色的亮度值。老实说,这不是一项艰巨的任务,但选择不同的底色需要创建一套全新的色调和色调。一次又一次手动执行此操作很快就会变得很费力。

我提到了 HSL——H(色调)、S(饱和度)和 L(亮度)——色彩空间,但这只是描述颜色的几种方法之一。 RGB——R(红色)、G(绿色)、B(蓝色)——可能是最熟悉的,至少是其十六进制形式。 还有 LAB — L(亮度)、A(绿-红)、B(蓝-黄) — 以及较新但现在得到广泛支持的 LCH — L(亮度)、C(色度)、H(色调) — OKLCH 形式的模型。使用 LCH(特别是 CSS 中的 OKLCH),我可以调整基础颜色的亮度值。

或者我可以改变它的色度。 LCH 色度和 HSL 饱和度都描述颜色的强度或丰富度,但它们的描述方式不同。 LCH 为我提供了更广泛、更可预测的颜色混合。

我还可以更改色调以创建具有相同亮度和色度值的调色板。在 HSL 和 LCH 中,色调光谱从红色开始,经过绿色和蓝色,最后返回红色。

为什么 OKLCH 改变了我对颜色的看法 尽管包括 Sketch 在内的设计工具尚未跟上,但浏览器对 OKLCH 色彩空间的支持现已广泛普及。幸运的是,这不会阻止您使用 OKLCH。浏览器会很乐意为您将 Hex、HSL、LAB 和 RGB 值转换为 OKLCH。您可以在任何空间(包括十六进制)中定义具有基础颜色的 CSS 自定义属性: /* 基础颜色 */ --基础:#5accd6;

从它派生的任何颜色都会自动转换为 OKLCH: --foundation-light: oklch(来自 var(--foundation) [...]; } --foundation-mid: oklch(来自 var(--foundation) [...]; } --foundation-dark: oklch(来自 var(--foundation) [...]; }

相对颜色作为设计系统 将相对颜色想象为:“采用这种颜色,调整它,然后给我结果。”调整颜色有两种方法:绝对变化和比例变化。它们在代码中看起来很相似,但一旦开始交换基础颜色,它们的行为就会非常不同。了解这种差异可以将相对颜色转变为一个系统。 /* 基础颜色 */ --基础:#5accd6;

例如,我的底色的亮度值为 0.7837,而较暗版本的值为 0.5837。为了计算差异,我用较高的值减去较低的值,然后使用 calc() 函数应用结果: --基础-黑暗: oklch(来自 var(--foundation) 计算(l - 0.20) c h);

为了获得更浅的颜色,我添加了差异: --基础光: oklch(来自 var(--foundation) 计算(l + 0.10) c h);

色度调整遵循相同的过程。为了将基础颜色的强度从 0.1035 降低到 0.0035,我从另一个值中减去一个值: oklch(来自 var(--foundation) l 计算(c - 0.10) h);

为了创建色调调色板,我计算了基础颜色 (200) 和新色调 (260) 的色调值之间的差异: oklch(来自 var(--foundation) l c 计算(h + 60));

这些计算是绝对的。当我减去一个固定的金额时,我实际上是在说:“总是减去这么多。”添加固定值时也是如此: 计算(c - 0.10) 计算(c + 0.10)

我经历了惨痛的教训才认识到这种方法的局限性。当我依靠减去固定色度值时,一旦我改变了基础,颜色就会变成灰色。适用于一种颜色的调色板不适用于另一种颜色。 乘法的行为有所不同。当我乘以色度时,我告诉浏览器:“按一定比例降低该颜色的强度。”即使基础发生变化,颜色之间的关系也保持不变: 计算(c * 0.10)

我的移动、缩放、旋转规则

移动亮度(加或减), 缩放色度(乘法), 旋转色调(增加或减少度数)。

我缩放色度是因为我希望强度变化与基色保持成比例。色调关系是旋转的,因此相乘色调没有意义。亮度是感性的、绝对的——乘以它常常会产生奇怪的结果。

从一种颜色到整个主题 相对颜色允许我定义基础颜色并从中生成我需要的所有其他颜色 - 填充、描边、渐变停止、阴影。到那时,颜色不再是调色板,而是开始成为一个系统。 SVG 插图倾向于在填充、描边和渐变中重复使用相同的几种颜色。相对颜色让您可以一次定义这些关系,然后在任何地方重复使用它们,就像动画师重复使用背景来创建新场景一样。

更改基础颜色一次,每种派生颜色都会自动更新,无需手动重新计算任何内容。除了动画图形之外,我还可以使用相同的方法来定义交互元素(例如按钮和链接)的状态颜色。 我在“Lullabye-Bye Bear”卡通标题中使用的底色是青色的蓝色。背景是我的粉底和较暗版本之间的径向渐变。

要创建具有完全不同心情的替代版本,我只需要更改基础颜色: --基础:#5accd6; --grad-end: var(--基础); --grad-start: oklch(来自 var(--foundation) 计算(l - 0.2357)计算(c * 0.833)h);

为了将这些自定义属性绑定到我的 SVG 渐变而不重复颜色值,我用内联样式替换了硬编码的停止颜色值:

<定义>

<路径 fill="url(#bg-grad)" fill="#5DCDD8" d="[...]"/>

接下来,我需要确保我的卡通文本始终与我选择的任何底色形成对比。 180 度的色调旋转会产生一种肯定会流行的互补色,但会产生令人不舒服的振动: .text-light { 填充:oklch(来自 var(--foundation) l c 计算(h + 180)); }

90° 偏移会产生生动的二次色,但不会完全互补: .text-light { 填充:oklch(来自 var(--foundation) l c 计算(h - 90)); }

我对 Quick Draw McGraw 1959 年的卡通标题“El Kabong”的重新制作使用了相同的技术,但调色板更加多样化。例如,基础颜色和较深的色调之间还有另一个径向渐变。

背景中的建筑物和树木只是相同基础颜色的不同深浅。对于这些路径,我需要两种额外的填充颜色: .bg-mid { 填充:oklch(来自 var(--foundation) 计算(l - 0.04)计算(c * 0.91)h); }

.bg-暗{ 填充:oklch(来自 var(--foundation) 计算(l - 0.12)计算(c * 0.64)h); }

当基础开始移动时 到目前为止,我展示的所有内容都是静态的。即使有人使用颜色选择器来更改基础颜色,这种变化也会立即发生。但动画图形很少是静止不动的——线索就在名字中。因此,如果颜色是系统的一部分,那么它也没有理由不能设置动画。 要对基础颜色进行动画处理,我首先需要将其拆分为 OKLCH 通道— 亮度、色度和色调。但还有一个重要的额外步骤:我需要将这些值注册为类型化自定义属性。但这意味着什么? 默认情况下,浏览器不知道 CSS 自定义属性值是否表示颜色、长度、数字或完全其他内容。这通常意味着它们无法在动画过程中顺利插值,并且无法从一个值跳转到下一个值。 注册自定义属性会告诉浏览器它所代表的值的类型以及它随着时间的推移应该如何表现。在这种情况下,我希望浏览器将我的颜色通道视为数字,以便它们可以顺利地进行动画处理。 @property --f-l { 语法:“<数字>”; 继承:真实; 初始值:0.40; }

@属性--f-c { 语法:“<数字>”; 继承:真实; 初始值:0.11; }

@property --f-h { 语法:“<数字>”; 继承:真实; 初始值:305; }

注册后,这些自定义属性的行为类似于本机 CSS。浏览器可以逐帧插入它们。然后我从这些通道重建基础颜色: --foundation: oklch(var(--f-l) var(--f-c) var(--f-h));

这使得基础颜色变得可动画,就像任何其他数值一样。这是一个简单的“呼吸”动画,它会随着时间的推移轻轻地改变亮度: @keyframes呼吸{ 0%, 100% { --f-l: 0.36; } 50% { --f-l: 0.46; } }

.toon-标题 { 动画:呼吸 10 秒,缓入无限; }

因为填充、渐变和描边中的所有其他颜色都源自 --foundation,所以它们都一起进行动画处理,并且不需要手动更新任何内容。 一种动画颜色,多种效果 在这个过程开始时,我想知道 CSS 相对颜色值是否可以提供更多可能性,同时也使它们更易于实现。我最近在网站的联系页面上添加了一个新的金矿背景,第一个迭代包括发光和摆动的油灯。

我想探索动画 CSS 相对颜色如何通过用灯的颜色着色来使矿井内部更加真实。我希望它们能够像真实的光一样影响周围的世界。因此,我没有构建多种颜色的动画,而是构建了一个仅对一种颜色进行动画处理的小型照明系统。

我的第一个任务是在背景和灯之间放置一个覆盖层: <路径 id=“覆盖” 填充=“var(--overlay-tint)” [...] 样式=“混合混合模式:颜色” />

我使用 mix-blend-mode: color 因为它可以对下面的内容进行着色,同时保留底层的亮度。由于我只希望在动画打开时叠加层可见,因此我选择了叠加层: .svg-mine #overlay { 显示:无; }

@media(更喜欢减少运动:无偏好){ .svg-mine[data-animations=on] #overlay { 显示:块; 不透明度:0.5; } }

覆盖层已就位,但尚未连接到灯。我需要一个光源。我的灯很简单,每个灯都包含一个圆形元素,我用滤镜模糊了它。滤镜在整个圆圈上产生非常柔和的模糊效果。

<过滤器id =“lamp-glow-1”x =“-120%”y =“-120%”宽度=“340%”高度=“340%”>

我没有分别对覆盖层和灯进行动画处理,而是对单个“火焰”颜色标记进行动画处理,并从中导出其他所有内容。首先,我为 OKLCH 通道注册三个类型化的自定义属性: @property --fl-l { 语法:“<数字>”; 继承:真实; 初始值:0.86; } @property --fl-c { 语法:“<数字>”; 继承:真实; 初始值:0.12; } @property --fl-h { 语法:“<数字>”; 继承:真实; 初始值:95; }

我对这些通道进行了动画处理,故意将几帧推向橙色,这样闪烁就可以清晰地读取为火光:

@keyframes火焰{ 0%, 100% { --fl-l: 0.86; --fl-c:0.12; --fl-h:95; } 6% { --fl-l: 0.91; --fl-c:0.10; --fl-h:92; } 12% { --fl-l: 0.83; --fl-c:0.14; --fl-h: 100; } 18% { --fl-l: 0.88; --fl-c:0.11; --fl-h:94; } 24% { --fl-l: 0.82; --fl-c:0.16; --fl-h: 82; } 30% { --fl-l: 0.90; --fl-c:0.12; --fl-h:90; } 36% { --fl-l: 0.79; --fl-c:0.17; --fl-h:76; } 44% { --fl-l: 0.87; --fl-c:0.12; --fl-h:96; } 52% { --fl-l: 0.81; --fl-c:0.15; --fl-h: 102; } 60% { --fl-l: 0.89; --fl-c:0.11; --fl-h:93; } 68% { --fl-l: 0.83; --fl-c:0.16; --fl-h:85; } 76% { --fl-l: 0.91; --fl-c:0.10; --fl-h:91; } 84% { --fl-l: 0.85; --fl-c:0.14; --fl-h:98; } 92% {--fl-l:0.80; --fl-c:0.17; --fl-h:74; } }

然后我将该动画范围限定为 SVG,因此共享变量可用于灯和我的叠加层:

@media(更喜欢减少运动:无偏好){ .svg-mine[数据动画=on] { 动画:火焰3.6s无限线性; 隔离的英文

/* 从动画通道构建火焰颜色 */ --flame: oklch(var(--fl-l) var(--fl-c) var(--fl-h));

/* 灯的颜色源自火焰 */ --lamp-core: oklch(来自 var(--flame) calc(l + 0.05) calc(c * 0.70) h);

/* 来自同一火焰的叠加色调 */ --overlay-tint: oklch(来自 var(--flame) 计算(l + 0.06) 计算(c * 0.65) 计算(h - 10)); } }

最后,我将这些派生颜色应用到发光灯及其影响的覆盖层上: @media(更喜欢减少运动:无偏好){ .svg-mine[data-animations=on] #mine-lamp-1 > 圆圈, .svg-mine[data-animations=on] #mine-lamp-2 > 圆 { 填充:var(--灯芯); }

.svg-mine[data-animations=on] #overlay { 显示:块; 填充:var(--overlay-tint); 不透明度:0.5; } }

当火焰变成橙色时,灯就会变暖,场景也会随之变暖。当火焰冷却时,一切都会安定下来。最好的部分是没有任何内容是手动编写的。如果我更改基础颜色或调整火焰动画范围,整个照明系统会同时更新。 你可以在我的网站上看到最终结果。 重用、改变用途、重新审视 那些汉纳-巴伯拉动画师出于必要而被迫重新调整元素的用途,但我重复使用颜色,因为它使我的作品更加一致且更易于维护。 CSS 相对颜色值允许我:

定义单一基础颜色, 描述其他颜色与它的关系, 到处重用这些关系,并且 通过更改一个值来动画系统。

相对颜色不仅使主题更容易。它鼓励一种思维方式,其中颜色就像运动一样是有意为之的,并且改变一个值可以改变整个场景,而无需重写其下面的工作。

You May Also Like

Enjoyed This Article?

Get weekly tips on growing your audience and monetizing your content — straight to your inbox.

No spam. Join 138,000+ creators. Unsubscribe anytime.

Create Your Free Bio Page

Join 138,000+ creators on Seemless.

Get Started Free