摄影 | Eliott Reyna
最近读到一篇文章《Everything you need to know about CSS Variables》,让我对 CSS 变量有了新的认识。下面将整理出来的跟自己感悟的地方与大家分享,希望能帮助到对 CSS 变量还一知半解的同学。
答疑
一、为什么 CSS 变量还称为“自定义属性(custom properties)”?
我们回忆 CSS 变量的使用方式:
span {
/* 局部变量 --color */
--color: gold;
color: var(--color);
}
声明 CSS 变量的时候,发现跟使用普通属性是一样的,这里说的“一样”是指:使用的位置一样,并且使用的方式也一样。
只不过跟普通属性相比,CSS 变量多了两个连字符 -- 作为前缀,本质上就是个属性。而且这类属性都是开发者自己起的,属性值也是我们设置的,自然就是“自定义属性”了。
不过特殊的地方在于,我们可以使用 var() 函数解析出这类属性的属性值:
color: var(--color);
/* 相当于 */
color: gold;
还有一点,CSS 变量既然是属性,那么就可以像行内样式那样使用:
<span style="--color: gold; color: var(--color);">这段文本是金色的</span>
文章后面“使用 JS 操作 CSS 变量”一节,就是基于此种写法实现的。之后会讲到,咱们先继续往下看。
二、全局变量和局部变量
全局变量是这样声明的:
:root {
--color: gray;
}
:root 伪类命中的是文档根元素 <html>,也就说
:root {
--color: gray;
}
/* 相当于 */
html {
--color: gray;
}
根元素是文档的最顶层元素,在它下面声明的变量就是全局变量。对应的,不是全局的变量就是局部变量。
<style>
:root {
/* 全局变量 --color */
--color: gray;
}
p {
/* 局部变量 --color */
--color: gold;
color: var(--color);
}
</style>
这段文本是灰色的,<span>这段文本是金色的</span>
与 JS 作用域类似的是,局部变量会覆盖全局中的同名变量。因此上面 <span> 里的文本是金色的。
三、var() 解析出来的结果只能作为属性值使用
下面这样写是不行的:
.mt-20px {
--mt: margin-top;
var(--mt): 20px; /* × 这种写法是错误的 */
}
我们想要可能是这种结果:margin-top: 20px,但观察发现,浏览器并不会解析 var(--mt),而且提示这是一个未知属性名。
下面这样写就没有问题了:
.mt-20px {
--20px: 20px;
margin-top: var(--20px); /* √ 这样写就没问题了 */
}
四、var() 的回退值(fallback)
var() 功能符,还接受第二个参数,表示一个回退值——当变量不能成功解析时,就会使用这个回退值。
.header {
color: var(--header-color, blue);
}
上面代码中,如果 --header-color 没有声明的话,就会使用回退值 blue,作用有点类似于 JS 函数中的参数默认值。
五、使用 calc() 做数学运算
如果声明的变量值中包含数学运算,就要包装在 calc() 函数中。否则是无效运算。
像下面这种写法就不对:
.font-40px {
--size: 20px * 2; /* × 错误的写法 */
font-size: var(--size);
}
在浏览器中观察,不会看见显式的报错,但 font-size 的最终解析值仍是默认的 16px。
这种错误写法在浏览器中并不会显式报错
`font-size` 的最终的解析值仍是 `16px`
需要这样写:
.font-40px {
--size: calc(20px * 2); /* √ 正确的写法 */
font-size: var(--size);
}
calc() 函数的引入,为在 CSS 中进行各种不同单位的混合数值运算(加、减、乘、除),带来了极大的便捷:
.example {
/* 加 */
width: calc(100% + 1em);
/* 减 */
width: calc(100% - 80px);
/* 乘 */
width: calc(100% * .5);
/* 除 */
width: calc(100% / 6);
}
这里抛砖引玉,更多的使用细节可以查看 MDN 上的文档。
六、使用 JS 操作 CSS 变量
JS 操作 CSS 变量的原理,是使用 DOM 对象的 style 属性,它是一个 CSSStyleDeclaration 类型的对象。
我们之前可能做过这样的操作:
document.body.style.color = 'gold'
color 是标准属性,可以直接使用这种方式设置。CSS 变量则属于非标准属性,使用这种方法就不会起作用:
// × 错误的写法,因为 --color 并不是标准属性
document.body.style['--color'] = 'gold'
但 CSSStyleDeclaration 上提供了一个 setProperty() 方法,可以用来设置非标准属性。语法如下:
style.setProperty(propertyName, value, priority);
因此,我们可以这么做:
document.body.style.setProperty('--color', 'gold');
执行结果如下:
这样,咱们就能通过 JS 操作 CSS 变量了。
实践
CSS 变量的使用,在一定程度上改变了我们书写、组织代码的形式。其原理在于,我们能够修改已有变量的值。
下面我举两个比较有代表性的案例:
- 主题按钮
- 元素的 transform 变换
主题按钮
我们有四个主题色的按钮,在不同的场景下使用。使用以前的写法,是通过覆盖属性的方式实现的:
.btn {
color: #333;
background-color: #eee;
border: 0;
padding: .5rem;
cursor: pointer;
}
.btn-success {
color: #fff;
background-color: green;
}
.btn-error {
color: #fff;
background-color: red;
}
.btn-warning {
background-color: orange;
}
如果使用变量,就不需要覆盖属性,直接修改变量值即可。
.btn {
color: var(--btn-color, #333);
background-color: var(--btn-bg-color, #eee);
border: 0;
padding: .5rem;
}
.btn-success {
--btn-color: #fff;
--btn-bg-color: green;
}
.btn-error {
--btn-color: #fff;
--btn-bg-color: red;
}
.btn-warning {
--btn-bg-color: orange;
}
demo 地址查看这里:codepen.io/zhangbao/pe…
元素的 transform 变换
先看看最终的效果图:
demo 地址:codepen.io/zhangbao/pe…
实现原理是这样的:我们在拖拉 Range Input 的时候,获取当前的 value 值,设置为变量 --slider 的值,.color-boxes 使用了此变量设置自身的 Y 轴偏移度。
涉及到的核心代码如下:
CSS:
.color-boxes {
transform: perspective(500px) rotateY( calc(var(--slider) * 1deg));
}
JS
const range = document.querySelector('.booth-slider')
range.addEventListener('input', handleSlider)
function handleSlider (e) {
document.documentElement.style.setProperty('--slider', e.target.value)
}
嗯,很神奇。
最后
本篇文章是我在看了一篇技术文章后总结的知识点,并加上了自己的一些感悟。如果看完后帮到了你,我将感到万分荣幸!😁
(完)