妙用CSS变量,让你的CSS变得更心动

 前言

十多年的安居网站建设经验,针对设计、前端、开发、售后、文案、推广等六对一服务,响应快,48小时及时工作处理。全网整合营销推广的优势是能够根据用户设备显示端的尺寸不同,自动调整安居建站的显示方式,使网站能够适用不同显示终端,在浏览器中调整网站的宽度,无论在任何一种浏览器上浏览网站,都能展现优雅布局与设计,从而大程度地提升浏览体验。成都创新互联公司从事“安居网站设计”,“安居网站推广”以来,每个客户项目都认真落实执行。

「CSS变量」又叫「CSS自定义属性」,为什么会突然提起这个很少人用到的东西呢?因为最近在重构个人官网,不知道为什么突然喜欢用上「CSS变量」,可能其自身隐藏的魅力,让笔者对它刮目相看。

谈到为什么会在CSS中使用变量,下面举个栗子,估计大家一看就会明白。

 
 
 
 
  1. /* 不使用CSS变量 */  
  2. .title {  
  3.     background-color: red;  
  4. }  
  5. .desc {  
  6.     background-color: red;  
  7. }  
  8. /* 使用CSS变量 */  
  9. :root {  
  10.     --bg-color: red;  
  11. }  
  12. .title {  
  13.     background-color: var(--bg-color);  
  14. }  
  15. .desc { 
  16.  
  17.     background-color: var(--bg-color);  

看完可能会觉得使用「CSS变量」的代码量多了一点,但是有没有想到突然某天万恶的策划小哥哥和设计小姐姐说要做一个换肤功能。按照平常的思路,估计有些同学就会按照默认颜色主题增加一份对照的新颜色主题CSS文件。这样每次新增需求都同时维护几套主题颜色多麻烦啊。

此时「CSS变量」就派上用场了,提前跟设计小姐姐规范好各种需要变换的颜色并通过「CSS变量」进行定义,通过JS批量操作这些定义好的「CSS变量」即可。这也是「变换主题颜色」的一种解决方案之一,好处在于只需写一套CSS代码。

 
 
 
 
  1. ["red", "blue", "green"].forEach(v => {  
  2.     const btn = document.getElementById(`${v}-theme-btn`);  
  3.     btn.addEventListener("click", () => document.body.style.setProperty("--bg-color", v));  
  4. }); 

在此总结下CSS使用变量的好处:

  •  减少样式代码的重复性
  •  增加样式代码的扩展性
  •  提高样式代码的灵活性
  •  增多一种CSS与JS的通讯方式
  •  不用深层遍历DOM改变某个样式

可能有些同学会问,Sass和Less早就实现了变量这个特性,何必再多此一举呢。可是细想一下,「CSS变量」对比Sass和Less的变量,又有它的过人之处。

  •  浏览器原生特性,无需经过任何转译就可直接运行
  •  DOM对象一员,极大便利了CSS与JS之间的联系

认识

本来打算用一半篇幅讲述「CSS变量」的规范和用法,但是网上一搜一大把就感觉没必要了,贴上阮一峰老师写的教程《CSS变量教程》。同时笔者也对「CSS变量」的细节地方进行一个整理,方便大家记忆。

  •  声明:--变量名
  •  读取:var(--变量名, 默认值)
  •  类型
    •   普通:只能用作属性值不能用作属性名
    •   字符:与字符串拼接 "Hello, "var(--name)
    •   数值:使用calc()与数值单位连用 var(--width) * 10px
  •  作用域
    •   范围:在当前元素块作用域及其子元素块作用域下有效
    •   优先级别:内联样式 > ID选择器 > 类选择器 = 属性选择器 = 伪类选择器 > 标签选择器 = 伪元素选择器

接下来使用几个特别的场景展示「CSS变量」的魅力。还是那句话,「一样东西有使用的场景,那自然就会有它的价值」,那么用的人也会越来越多。

使用场景

其实「CSS变量」有一个特别好用的场景,那就是结合List元素集合使用。如果不明白这是什么,请继续往下看。

 
 
 
 
  1. 以下所有演示代码基于Vue文件,但HTML、CSS和JS分开书写,为了简化CSS的书写而使用Sass进行预处理,方便代码演示 

条形加载条

一个条形加载条通常由几条线条组成,并且每条线条对应一个存在不同时延的相同动画,通过时间差运行相同的动画,从而产生加载效果。估计大部分的同学可能会把CSS代码写成以下这样。

 
 
 
 
  1.   
  2.     
  3.  
  4.   
 
 
 
 
  1. .loading {  
  2.     width: 200px;  
  3.     height: 200px;  
  4.     li {  
  5.         border-radius: 3px;  
  6.         width: 6px;  
  7.         height: 30px;  
  8.         background-color: #f66;  
  9.         animation: beat 1s ease-in-out infinite;  
  10.         & + li {  
  11.             margin-left: 5px;  
  12.         }  
  13.         &:nth-child(2) {  
  14.             animation-delay: 200ms;  
  15.         }  
  16.         &:nth-child(3) {  
  17.             animation-delay: 400ms;  
  18.         }  
  19.         &:nth-child(4) {  
  20.             animation-delay: 600ms;  
  21.         }  
  22.         &:nth-child(5) {  
  23.             animation-delay: 800ms;  
  24.         }  
  25.         &:nth-child(6) {  
  26.             animation-delay: 1s;  
  27.         }  
  28.     }  

分析代码发现,每个

  • 只是存在animation-delay不同,而其余代码则完全相同,换成其他类似的List元素集合场景,那岂不是有10个
  • 就写10个:nth-child。

    显然这种方法不灵活也不容易封装成组件,如果能像JS那样封装成一个函数,并根据参数输出不同的样式效果,那就更棒了。说到这里,很明显就是为了铺垫「CSS变量」的开发技巧了。

    对于HTML部分的修改,让每个

  • 拥有一个自己作用域下的「CSS变量」。对于CSS部分的修改,就需要分析哪些属性是随着index递增而发生规律变化的,对规律变化的部分使用「CSS变量」表达式代替即可。

     
     
     
     
    1.   
    2.     
    3.   
    4.   
     
     
     
     
    1. .strip-loading {  
    2.     width: 200px;  
    3.     height: 200px;  
    4.     li {  
    5.         --time: calc((var(--line-index) - 1) * 200ms);  
    6.         border-radius: 3px;  
    7.         width: 6px;  
    8.         height: 30px;  
    9.         background-color: #f66;  
    10.         animation: beat 1.5s ease-in-out var(--time) infinite;  
    11.         & + li {  
    12.             margin-left: 5px;  
    13.         }  
    14.     }  
    15. }  
     
     
     
     
    1. 源码链接可在文章结尾处获取 

    代码中的变量--line-index和--time使每个

  • 拥有一个属于自己的作用域。例如第2个
  • ,--line-index的值为2,--time的计算值为200ms,换成第3个
  • 后这两个值又会不同了。

    这就是「CSS变量」的作用范围所致(在当前元素块作用域及其子元素块作用域下有效),因此在.strip-loading的块作用域下调用--line-index是无效的。

     
     
     
     
    1. /* flex属性无效 */  
    2. .loading {  
    3.     display: flex;  
    4.     align-items: center;  
    5.     flex: var(--line-index);  

    通过妙用「CSS变量」,也把CSS代码从29行缩减到15行,对于那些含有List元素集合越多的场景,效果就更明显。而且这样写也更加美观更加容易维护,某天说加载效果的时间差不明显,直接将calc((var(--line-index) - 1) * 200ms)里的200ms调整成400ms即可。就无需对每个:nth-child(n)进行修改了。

    心形加载条

    前段时间刷掘金看到陈大鱼头兄的心形加载条,觉得挺漂亮的,很带感觉。

    通过动图分析,发现每条线条的背景色和动画时延不一致,另外动画运行时的高度也不一致。细心的你可能还会发现,第1条和第9条的高度一致,第2条和第8条的高度一致,依次类推,得到高度变换相同类的公式:对称index = 总数 + 1 - index。

    背景色使用了滤镜的色相旋转hue-rotate函数,目的是为了使颜色过渡得更加自然;动画时延的设置和上面条形加载条的设置一致。下面就用「CSS变量」根据看到的动图实现一番。

     
     
     
     
    1.   
    2.       
    3.         
    4.   
    5.       
  •   
     
     
     
     
    1. .heart-loading {  
    2.     width: 200px;  
    3.     height: 200px;  
    4.     ul {  
    5.         display: flex;  
    6.         justify-content: space-between;  
    7.         width: 150px;  
    8.         height: 10px;  
    9.     }  
    10.     li {  
    11.         --Θ: calc(var(--line-index) / var(--line-count) * .5turn);  
    12.         --time: calc((var(--line-index) - 1) * 40ms);  
    13.         border-radius: 5px;  
    14.         width: 10px;  
    15.         height: 10px;  
    16.         background-color: #3c9;  
    17.         filter: hue-rotate(var(--Θ));  
    18.         animation-duration: 1s;  
    19.         animation-delay: var(--time);  
    20.         animation-iteration-count: infinite;  
    21.     }  
    22.     .line-1,  
    23.     .line-9 {  
    24.         animation-name: line-move-1;  
    25.     }  
    26.     .line-2,  
    27.     .line-8 {  
    28.         animation-name: line-move-2;  
    29.     }  
    30.     .line-3,  
    31.     .line-7 {  
    32.         animation-name: line-move-3;  
    33.     }  
    34.     .line-4,  
    35.     .line-6 {  
    36.         animation-name: line-move-4;  
    37.     }  
    38.     .line-5 {  
    39.         animation-name: line-move-5;  
    40.     }  
    41. }  
     
     
     
     
    1. 源码链接可在文章结尾处获取 

    一波操作后就有了下面的效果。和陈大鱼头兄的心形加载条对比一下,颜色、波动曲线和跳动频率有点不一样,在暖色调的蔓延和肾上腺素的飙升下,这是一种心动的感觉。想起自己曾经写的一首诗:我见犹怜,爱不释手,雅俗共赏,君子好逑。

    标签导航栏

    上面通过两个加载条演示了「CSS变量」在CSS中的运用以及一些妙用技巧,现在通过标签导航栏演示「CSS变量」在JS中的运用。

    JS中主要有3个操作「CSS变量」的API,看上去简单易记,分别如下:

    先上效果图,效果中主要是使用「CSS变量」标记每个Tab的背景色和切换Tab的显示状态。

     
     
     
     
    1.   
    2.     
    3.         标题{{i + 1}}  
    4.       
    5.     
        
    6.           
    7.             内容{{i + 1}}
    8.   
    9.           
    10.     
      
  •   
     
     
     
     
    1. .tab-navbar {  
    2.     display: flex;  
    3.     overflow: hidden;  
    4.     flex-direction: column-reverse;  
    5.     border-radius: 10px;  
    6.     width: 300px;  
    7.     height: 400px;  
    8.     nav {  
    9.         display: flex;  
    10.         height: 40px;  
    11.         background-color: #f0f0f0;  
    12.         line-height: 40px;  
    13.         text-align: center;  
    14.         a {  
    15.             flex: 1;  
    16.             cursor: pointer;  
    17.             transition: all 300ms;  
    18.             &.active {  
    19.                 background-color: #66f;  
    20.                 font-weight: bold;  
    21.                 color: #fff;  
    22.             }  
    23.         }  
    24.     }  
    25.     div {  
    26.         flex: 1;  
    27.         ul {  
    28.             --tab-index: 0; 
    29.              --tab-width: calc(var(--tab-count) * 100%);  
    30.             --tab-move: calc(var(--tab-index) / var(--tab-count) * -100%);  
    31.             display: flex;  
    32.             flex-wrap: nowrap;  
    33.             width: var(--tab-width);  
    34.             height: 100%;  
    35.             transform: translate3d(var(--tab-move), 0, 0);  
    36.             transition: all 300ms;  
    37.         }  
    38.         li {  
    39.             display: flex;  
    40.             justify-content: center;  
    41.             align-items: center;  
    42.             flex: 1;  
    43.             background-color: var(--bg-color);  
    44.             font-weight: bold;  
    45.             font-size: 20px;  
    46.             color: #fff;  
    47.         }  
    48.     }  
    49. }  
     
     
     
     
    1. exportdefault {  
    2.     data() {  
    3.         return {  
    4.             index: 0,  
    5.             list: ["#f66", "#09f", "#3c9"]  
    6.         };  
    7.     },  
    8.     methods: {  
    9.         select(i) {  
    10.             this.index = i;  
    11.             this.$refs.tabs.style.setProperty("--tab-index", i);  
    12.         }  
    13.     }  
    14. };  
     
     
     
     
    1. 源码链接可在文章结尾处获取