有时为了做一些特殊的效果,我们需要给大量(至少也是十几个)元素不同的样式,而且往往这些样式正好跟元素的顺序有一定的数学关系,甚至说可以根据元素的顺序直接计算出样式。
比如说用css做一个时钟(这是我每期的css作业之一):

这里有12个对应小时的数字和60个对应分钟的刻度,如果不使用任何工具写这么多元素的样式肯定会疯掉(最主要这些样式正好跟元素的顺序有线性关系),所以这里我们往往会用sass的循环(别提less的循环)配合:nth-child和简单的运算来批量生成每个元素的样式:
@for $i from 1 through 12 {
span:nth-child(#{$i}) {
transform: rotate($i * 30deg) translate(calc(var(--radius) - 12px)) rotate($i * -30deg) rotate(90deg);
}
}
生成出以下代码
span:nth-child(1) {
transform: rotate(30deg) translate(calc(var(--radius) - 12px)) rotate(-30deg) rotate(90deg);
}
span:nth-child(2) {
transform: rotate(60deg) translate(calc(var(--radius) - 12px)) rotate(-60deg) rotate(90deg);
}
span:nth-child(3) {
transform: rotate(90deg) translate(calc(var(--radius) - 12px)) rotate(-90deg) rotate(90deg);
}
span:nth-child(4) {
transform: rotate(120deg) translate(calc(var(--radius) - 12px)) rotate(-120deg) rotate(90deg);
}
...
当项目内有使用sass时,这么写方便倒是方便,但生成出来的代码也忒多了,而且如果有多个这样的情况,比如上面的数字+刻度,代码就会多出很多了。
而如果只是写点演示代码,还得找个网站把sass代码转成css后粘贴过来。
我今天想到了一个办法可以不使用sass,并且还可以做到不管多少这种情况,代码量都几乎不增长
其实我们就只是想在样式代码中能得到元素的索引并参与样式的计算,计算现在有calc(),我们需要的就是元素的索引,但css本身并没有办法能拿到元素的索引(不过现在css现在一天一个样,目前能不能拿到我还真不知道,万一哪天能拿到了也说不定),但现在我们有了css变量!完全可以通过css变量为每个顺序的元素设置索引,我们只需要写出如下代码就可以为【所有的元素】设置索引了,对你没看错,就是所有的元素,因为选择器只设置了:nth-child,没有其它限定,所以任何元素元素都可以通过var(--index)得到自己的顺序:
:nth-child(1) {
--index: 0;
}
:nth-child(2) {
--index: 1;
}
:nth-child(3) {
--index: 2;
}
:nth-child(4) {
--index: 3;
}
:nth-child(5) {
--index: 4;
}
:nth-child(6) {
--index: 5;
}
:nth-child(7) {
--index: 6;
}
:nth-child(8) {
--index: 7;
}
:nth-child(9) {
--index: 8;
}
:nth-child(10) {
--index: 9;
}
/* 需要多少就写多少,我觉得100个应该肯定够了,上面时钟也只需要60个 */
然后我们就可以在任何元素中使用var(--index)来引用到元素的索引了,如果有必要,也完全可以放进calc()中参与数学运算并得到针对每个元素的样式。比如要写这60个刻度的样式,只要这一句:
span {
/* 其它样式省略 */
transform: rotate(calc(var(--index) * 6deg)) translate(56px));
}
要写12个小时对应的数字也只需要一行代码:
span {
transform: rotate(calc(30deg * var(--index))) translate(52px)) rotate(calc(-30deg * var(--index))) rotate(90deg);
}
/* 这个代码比较长是因为它转的比较复杂并且省略了其它代码,所以看不懂也很正常,但用css变量的代码差不多都长这样 */
是的你没看错,不再需要:nth-child了,也不再需要分别选择每个元素了,只有一个选择器和一行代码,设定60个元素的样式并且最终每个元素都不一样。
而为了让这样的代码工作,你需要的只是在代码中加入那段通过:nth-child()设置元素自定义属性的静态代码。
实际上我一直以来还老想让css能够拿到元素的属性值并参与样式的计算,有了上面的思路其实就简单了,写一堆属性选择器来设置相应的css变量即可,如果担心写太多,也完全可以用tailwind配置,写了哪些就生成哪些。
关于兼容性,支持css变量的浏览器都支持calc,:nth-child就更不用说了。
关于体积的问题,我测了一下,100个这样的选择器minify后是2582个字节(csso),实际上如此大量雷同的代码,gzip压缩率应该至少是过半的(实测414个字节)。但实际上只是把index换成i就能立减400个字节(gzip后是399字节)。

