您可能已经对 CSS 变量有所了解。如果不是,这里有一个两秒钟的概述:它们实际上称为自定义属性,您可以在声明块中设置它们,例如 --size: 1em
,并将它们用作值,例如 font-size: var(--size);
,它们 与预处理器变量不同(例如,它们会级联),并且这里有 更多信息。
但是,我们是否充分利用了它们?我们是否陷入旧的习惯,忽略了变量可以显著减少我们编写的代码量的机会?
这篇文章的灵感来自于我最近发布的一条关于使用 CSS 变量创建动态动画行为的推文。
CSS 变量很棒,对吧?但是范围功能经常被忽视。例如,看看这个演示,3 个不同的动画,但只定义了 1 个动画 💪 这意味着动态动画 😎 https://#/VN02NlC4G8 via @CodePen #CSS #animation #webdev #webdesign #coding pic.twitter.com/ig8baxr7F3
— Jhey @ NodeConfEU 2019 📍🇮🇪⌨️ (@jh3yy) 2019 年 11 月 5 日
让我们看几个 CSS 变量可以用来做一些我们可能没有考虑过的非常酷的事情的例子。
基本范围优势
最简单且可能最常见的例子是范围颜色。我们最喜欢的用来搭配颜色的组件是什么?按钮。😅
考虑一下主要按钮和辅助按钮的标准设置。让我们从使用 BEM 语法 的一些基本标记开始。
<button class="button button--primary">Primary</button>
<button class="button button--secondary">Secondary</button>
传统上,我们可能会使用以下方法来对它们进行样式设置
.button {
padding: 1rem 1.25rem;
color: #fff;
font-weight: bold;
font-size: 1.25rem;
margin: 4px;
transition: background 0.1s ease;
}
.button--primary {
background: hsl(233, 100%, 50%);
outline-color: hsl(233, 100%, 80%);
}
.button--primary:hover {
background: hsl(233, 100%, 40%);
}
.button--primary:active {
background: hsl(233, 100%, 30%);
}
.button--secondary {
background: hsl(200, 100%, 50%);
outline-color: hsl(200, 100%, 80%);
}
.button--secondary:hover {
background: hsl(200, 100%, 40%);
}
.button--secondary:active {
background: hsl(200, 100%, 30%);
}
对于这样一件并不特别复杂的事情,这实在太多了。我们没有添加很多样式,而且添加了很多规则来适应按钮的不同状态和颜色。我们可以使用范围变量来显著减少代码量。
在我们的示例中,两个按钮变体之间唯一的不同值是色调。然后让我们稍微重构一下代码。我们不会更改标记,但会稍微清理一下样式,得到以下结果
.button {
padding: 1rem 1.25rem;
color: #fff;
font-weight: bold;
font-size: 1.25rem;
margin: 1rem;
transition: background 0.1s ease;
background: hsl(var(--hue), 100%, 50%);
outline-color: hsl(var(--hue), 100%, 80%);
}
.button:hover {
background: hsl(var(--hue), 100%, 40%);
}
.button:active {
background: hsl(var(--hue), 100%, 30%);
}
.button--primary {
--hue: 233;
}
.button--secondary {
--hue: 200;
}
这不仅减少了代码量,而且使维护变得更加容易。在同一个地方更改核心按钮样式,它将更新所有变体!🙌
我可能会保留它,以方便想要使用这些按钮的开发人员。但是,我们可以更进一步。我们可以将变量内联到实际元素上,并完全删除类声明。😲
<button class="button" style="--hue: 233;">Primary</button>
<button class="button" style="--hue: 200;">Secondary</button>
现在我们不再需要这些了。👍
.button--primary {
--hue: 233;
}
.button--secondary {
--hue: 200;
}
将这些变量内联可能不适合您下一个设计系统或应用程序,但它确实打开了新的可能性。例如,如果我们有一个需要覆盖颜色的按钮实例。
button.button.button--primary(style=`--hue: 20;`) Overridden
享受内联变量的乐趣
另一个机会是享受一下它带来的乐趣。这是我在 CodePen 上创建的许多 Pens 中使用的技巧。😉
您可能正在编写简单的 HTML,但在很多情况下,您可能正在使用 React 等框架或 Pug 等预处理器来编写您的标记。这些解决方案允许您利用 JavaScript 来创建随机的内联变量。对于以下示例,我将使用 Pug。Pug 是一个基于缩进的 HTML 模板引擎。如果您不熟悉 Pug,请不要担心!我会尽量保持标记的简单性。
让我们从为我们的按钮随机化色调开始
button.button(style=`--hue: ${Math.random() * 360}`) First
使用 Pug,我们可以使用 ES6 模板字面量来内联随机化的 CSS 变量。💪
动画更改
因此,现在我们有机会为元素定义随机特征,我们还能做什么?嗯,一个被忽视的机会是动画。没错,我们无法像这样直接为变量设置动画
@keyframes grow {
from { --scale: 1; }
to { --scale: 2; }
}
但我们可以基于范围变量创建动态动画。我们可以动态地更改动画行为!🤩
示例 1:兴奋的按钮
让我们创建一个按钮,它会漂浮在自己的地方,然后在我们悬停在其上时变得兴奋。
从标记开始
button.button(style=`--hue: ${Math.random() * 360}`) Show me attention
一个简单的浮动动画可能看起来像这样
@keyframes flow {
0%, 100% {
transform: translate(0, 0);
}
50% {
transform: translate(0, -25%);
}
}
这将给我们类似于下面的东西
我添加了一点阴影作为补充,但这不是必要的。👍
让我们让按钮在我们悬停在其上时变得兴奋。现在,我们可以简单地将正在使用的动画更改为类似于以下内容
.button:hover {
animation: shake .1s infinite ease-in-out;
}
@keyframes shake {
0%, 100% {
transform: translate(0, 0) rotate(0deg);
}
25% {
transform: translate(-1%, 3%) rotate(-2deg);
}
50% {
transform: translate(1%, 2%) rotate(2deg);
}
75% {
transform: translate(1%, -2%) rotate(-1deg);
}
}
它可以工作
但是,我们需要引入另一个关键帧定义。如果我们能将这两个动画合并成一个呢?就结构而言,它们并不相差太多。
我们可以尝试
@keyframes flow-and-shake {
0%, 100% {
transform: translate(0, 0) rotate(0deg);
}
25%, 75% {
transform: translate(0, -12.5%) rotate(0deg);
}
50% {
transform: translate(0, -25%) rotate(0deg);
}
}
虽然这可以工作,但由于平移步骤,我们最终得到一个不太平滑的动画。那么我们还能做什么?让我们通过删除 25% 和 75% 处的步骤来找到折衷方案。
@keyframes flow-and-shake {
0%, 100% {
transform: translate(0, 0) rotate(0deg);
}
50% {
transform: translate(0, -25%) rotate(0deg);
}
}
它按预期工作,但现在让我们用一些变量来更新我们的按钮。
.button {
--y: -25;
--x: 0;
--rotation: 0;
--speed: 2;
}
现在让我们将它们插入动画定义中,以及按钮的动画属性。
.button {
animation-name: flow-and-shake;
animation-duration: calc(var(--speed) * 1s);
animation-iteration-count: infinite;
animation-timing-function: ease-in-out;
}
@keyframes flow-and-shake {
0%, 100% {
transform: translate(calc(var(--x) * -1%), calc(var(--y) * -1%))
rotate(calc(var(--rotation) * -1deg));
}
50% {
transform: translate(calc(var(--x) * 1%), calc(var(--y) * 1%))
rotate(calc(var(--rotation) * 1deg));
}
}
一切正常。👍
让我们在悬停按钮时更改这些值
.button:hover {
--speed: .1;
--x: 1;
--y: -1;
--rotation: -1;
}
不错!现在我们的按钮具有两种不同的动画类型,但它们通过一组关键帧定义。🤯
让我们玩得更开心一点。如果我们更进一步,我们可以让按钮更具趣味性,并且可能在它处于活动状态时完全停止动画。😅
示例 2:气泡
现在我们已经了解了一些使用范围功能可以实现的不同技巧,让我们将它们全部整合在一起。我们将创建一个随机生成的气泡场景,该场景大量利用范围 CSS 变量。
让我们从创建一个气泡开始。一个静态气泡。

.bubble {
background: radial-gradient(100% 115% at 25% 25%, #fff, transparent 33%),
radial-gradient(15% 15% at 75% 75%, #80dfff, transparent),
radial-gradient(100% 100% at 50% 25%, transparent, #66d9ff 98%);
border: 1px solid #b3ecff;
border-radius: 100%;
height: 50px;
width: 50px;
}
我们使用 background
以及多个值和一个 border
来创建气泡效果——但这并不是很动态。我们知道 border-radius
将始终保持相同。我们还知道 border
和 background
的结构不会改变。但是,这些属性中使用的值以及其他属性值都可以是随机的。
让我们重构 CSS 以使用变量
.bubble {
--size: 50;
--hue: 195;
--bubble-outline: hsl(var(--hue), 100%, 50%);
--bubble-spot: hsl(var(--hue), 100%, 75%);
--bubble-shade: hsl(var(--hue), 100%, 70%);
background: radial-gradient(100% 115% at 25% 25%, #fff, transparent 33%),
radial-gradient(15% 15% at 75% 75%, var(--bubble-spot), transparent),
radial-gradient(100% 100% at 50% 25%, transparent, var(--bubble-shade) 98%);
border: 1px solid var(--bubble-outline);
border-radius: 100%;
height: calc(var(--size) * 1px);
width: calc(var(--size) * 1px);
}
这是一个很好的开始。👍
让我们添加更多气泡,并利用内联范围来定位和调整它们的大小。由于我们将开始随机化多个值,因此使用一个函数来为我们的标记生成范围内的随机数非常方便。
- const randomInRange = (max, min) => Math.floor(Math.random() * (max - min + 1)) + min
使用 Pug,我们可以利用迭代来创建一大组气泡
- const baseHue = randomInRange(0, 360)
- const bubbleCount = 50
- let b = 0
while b < bubbleCount
- const size = randomInRange(10, 50)
- const x = randomInRange(0, 100)
.bubble(style=`--x: ${x}; --size: ${size}; --hue: ${baseHue}`)
- b++
更新我们的 .bubble
样式允许我们利用新的内联变量。
.bubble {
left: calc(var(--x) * 1%);
position: absolute;
transform: translate(-50%, 0);
}
这将给我们一组随机的气泡
让我们更进一步,为这些气泡设置动画,使其从上到下浮动并淡出。
.bubble {
animation: float 5s infinite ease-in-out;
top: 100%;
}
@keyframes float {
from {
opacity: 1;
transform: translate(0, 0) scale(0);
}
to {
opacity: 0;
transform: translate(0, -100vh) scale(1);
}
}
这太无聊了。它们都在同一时间做同样的事情。因此,让我们随机化每个气泡的移动速度、延迟、结束缩放比例和移动距离。
- const randomInRange = (max, min) => Math.floor(Math.random() * (max - min + 1)) + min
- const baseHue = randomInRange(0, 360)
- const bubbleCount = 50
- let b = 0
while b < bubbleCount
- const size = randomInRange(10, 50)
- const delay = randomInRange(1, 10)
- const speed = randomInRange(2, 20)
- const distance = randomInRange(25, 150)
- const scale = randomInRange(100, 150) / 100
- const x = randomInRange(0, 100)
.bubble(style=`--x: ${x}; --size: ${size}; --hue: ${baseHue}; --distance: ${distance}; --speed: ${speed}; --delay: ${delay}; --scale: ${scale}`)
- b++
现在,让我们更新我们的样式
.bubble {
animation-name: float;
animation-duration: calc(var(--speed) * 1s);
animation-delay: calc(var(--delay) * -1s);
animation-iteration-count: infinite;
animation-timing-function: ease-in-out;
}
@keyframes float {
from {
opacity: 1;
transform: translate(-50%, 0) scale(0);
}
to {
opacity: 0;
transform: translate(-50%, calc(var(--distance) * -1vh)) scale(var(--scale));
}
}
然后我们将得到以下结果
通过大约 50 行代码,您可以通过利用范围的力量来创建一个随机生成的有动画场景!💪
就是这样!
通过充分利用 CSS 变量并利用一些小技巧,我们可以用很少的代码创建一些非常酷的东西。
我希望这篇文章能提高大家对 CSS 变量范围功能的认识,也希望大家能够充分利用它并将其传递下去 😎
这篇文章中的所有演示都在 这个 CodePen 集合 中。
喜欢这篇文章。
但是,我仍然有使用 IE11 的企业客户。
气泡面板都是黑色的……
我一直使用这个 polyfill,因为它很棒!https://npmjs.net.cn/package/css-vars-ponyfill
只要这种效果的包含是渐进增强,也许是在静态背景图片之上,以及在
@supports
中,它不应该对 IE11 及更早版本造成任何问题(尽管向客户解释这一点是另一个完全不同的问题)。我个人还没有尝试过,但这个 polyfill 看起来真的很不错
https://github.com/nuxodin/ie11CustomProperties
你为什么要在企业网站上使用气泡?而且 IE11 本来就很糟糕。像微软建议的那样,鼓励他们从 IE11 迁移。无论微软是否进行安全更新,他们都不会添加任何功能。
太棒了。我已经用这个技术重构了很多我的作品 :)
太棒了 @alphardex!我很高兴它对你有用 :D
谢谢!这真是太及时了。我今天就用它来解决了一个问题。
我不知道 CSS 变量还可以作用域!
没问题 @Shannon!我很高兴你能用它来解决问题 :D
作用域确实很有力量!
我绝对喜欢 CSS 变量。它比 SASS 变量灵活得多,而且你甚至不需要预处理器。可惜的是,目前只能在我的私人项目或 Electron 应用程序中使用,因为我所在的公司仍然需要支持 IE11。
一个小小的挑剔:完整的圆圈已经可以用
而不是
结果是一样的,但我还是想澄清一下。
嘿 Tim!
太可惜了。但至少你在某些地方可以玩它 :D
是的,完全同意。我想这是我的一个坏习惯哈哈。我总是写
100%
。好文章,继续努力,分享这样的文章,信息量很大。这篇文章中提供了很多高质量的信息。必读!!
谢谢 Aquadsoft!我真的很感谢。我会尽力而为!:D
哇!太棒了!
喜欢这篇文章,有很多东西要学习。我将
--hue
自定义属性添加到了.button__shadow
元素中,因为阴影在从投射阴影的对象中获取颜色时看起来更真实