百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

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

myzbx 2025-02-28 16:38 35 浏览



前言

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

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

/* 不使用CSS变量 */
.title {
    background-color: red;
}
.desc {
    background-color: red;
}

/* 使用CSS变量 */
:root {
    --bg-color: red;
}
.title {
    background-color: var(--bg-color);
}
.desc {
    background-color: var(--bg-color);
}
复制代码

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

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

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

在此总结下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元素集合使用。如果不明白这是什么,请继续往下看。

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

条形加载条

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



复制代码
.loading {
    width: 200px;
    height: 200px;
    li {
        border-radius: 3px;
        width: 6px;
        height: 30px;
        background-color: #f66;
        animation: beat 1s ease-in-out infinite;
        & + li {
            margin-left: 5px;
        }
        &:nth-child(2) {
            animation-delay: 200ms;
        }
        &:nth-child(3) {
            animation-delay: 400ms;
        }
        &:nth-child(4) {
            animation-delay: 600ms;
        }
        &:nth-child(5) {
            animation-delay: 800ms;
        }
        &:nth-child(6) {
            animation-delay: 1s;
        }
    }
}
复制代码

分析代码发现,每个

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

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

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

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

    复制代码
    .strip-loading {
        width: 200px;
        height: 200px;
        li {
            --time: calc((var(--line-index) - 1) * 200ms);
            border-radius: 3px;
            width: 6px;
            height: 30px;
            background-color: #f66;
            animation: beat 1.5s ease-in-out var(--time) infinite;
            & + li {
                margin-left: 5px;
            }
        }
    }
    复制代码

    源码链接可在文章结尾处获取

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

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

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

    /* flex属性无效 */
    .loading {
        display: flex;
        align-items: center;
        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变量根据看到的动图实现一番。

    复制代码
    .heart-loading {
        width: 200px;
        height: 200px;
        ul {
            display: flex;
            justify-content: space-between;
            width: 150px;
            height: 10px;
        }
        li {
            --Θ: calc(var(--line-index) / var(--line-count) * .5turn);
            --time: calc((var(--line-index) - 1) * 40ms);
            border-radius: 5px;
            width: 10px;
            height: 10px;
            background-color: #3c9;
            filter: hue-rotate(var(--Θ));
            animation-duration: 1s;
            animation-delay: var(--time);
            animation-iteration-count: infinite;
        }
        .line-1,
        .line-9 {
            animation-name: line-move-1;
        }
        .line-2,
        .line-8 {
            animation-name: line-move-2;
        }
        .line-3,
        .line-7 {
            animation-name: line-move-3;
        }
        .line-4,
        .line-6 {
            animation-name: line-move-4;
        }
        .line-5 {
            animation-name: line-move-5;
        }
    }
    复制代码

    源码链接可在文章结尾处获取

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



    标签导航栏

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

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

    • 读取变量:elem.style.getPropertyValue()
    • 设置变量:elem.style.setProperty()
    • 删除变量:elem.style.removeProperty()

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



    • 内容{{i + 1}}
    复制代码
    .tab-navbar {
        display: flex;
        overflow: hidden;
        flex-direction: column-reverse;
        border-radius: 10px;
        width: 300px;
        height: 400px;
        nav {
            display: flex;
            height: 40px;
            background-color: #f0f0f0;
            line-height: 40px;
            text-align: center;
            a {
                flex: 1;
                cursor: pointer;
                transition: all 300ms;
                &.active {
                    background-color: #66f;
                    font-weight: bold;
                    color: #fff;
                }
            }
        }
        div {
            flex: 1;
            ul {
                --tab-index: 0;
                --tab-width: calc(var(--tab-count) * 100%);
                --tab-move: calc(var(--tab-index) / var(--tab-count) * -100%);
                display: flex;
                flex-wrap: nowrap;
                width: var(--tab-width);
                height: 100%;
                transform: translate3d(var(--tab-move), 0, 0);
                transition: all 300ms;
            }
            li {
                display: flex;
                justify-content: center;
                align-items: center;
                flex: 1;
                background-color: var(--bg-color);
                font-weight: bold;
                font-size: 20px;
                color: #fff;
            }
        }
    }
    复制代码
    export default {
        data() {
            return {
                index: 0,
                list: ["#f66", "#09f", "#3c9"]
            };
        },
        methods: {
            select(i) {
                this.index = i;
                this.$refs.tabs.style.setProperty("--tab-index", i);
            }
        }
    };
    复制代码

    源码链接可在文章结尾处获取

      上定义--tab-index表示Tab当前的索引,当点击按钮时重置--tab-index的值,就可实现不操作DOM来移动
        的位置显示指定的Tab。不操作DOM而可移动
          是因为定义了--tab-move,通过calc()计算--tab-index与--tab-move的关系,从而操控transform: translate3d()来移动

            另外在

          • 上定义--bg-color表示Tab的背景色,也是一种比较简洁的模板赋值方式,总比写
          • 要好看。如果多个CSS属性依赖一个变量赋值,那么使用CSS变量赋值到style上就更方便了,那些CSS属性可在CSS文件里进行计算与赋值,这样可帮助JS分担一些属性计算工作。

            当然,这个标签导航栏也可通过纯CSS实现,有兴趣的同学可看看笔者之前一篇文章里的纯CSS标签导航栏。

            悬浮跟踪按钮

            通过几个栗子实践了CSS变量在CSS和JS上的运用,相信大家已经掌握了其用法和技巧。之前在某个网站看过一个比较酷炫的鼠标悬浮特效,好像也是使用CSS变量实现的。笔者凭着记忆也使用CSS变量实现一番。

            其实思路也比较简单,先对按钮进行布局和着色,然后使用伪元素标记鼠标的位置,定义--x和--y表示伪元素在按钮里的坐标,通过JS获取鼠标在按钮上的offsetLeft和offsetLeft分别赋值给--x和--y,再对伪元素添加径向渐变的背景色,大功告成,一个酷炫的鼠标悬浮跟踪特效就这样诞生了。



            
                妙用CSS变量,让你的CSS变得更心动
            
            复制代码
            .track-btn {
                display: block;
                overflow: hidden;
                border-radius: 100px;
                width: 400px;
                height: 50px;
                background-color: #66f;
                line-height: 50px;
                cursor: pointer;
                font-weight: bold;
                font-size: 18px;
                color: #fff;
                span {
                    position: relative;
                }
                &::before {
                    --size: 0;
                    position: absolute;
                    left: var(--x);
                    top: var(--y);
                    width: var(--size);
                    height: var(--size);
                    background-image: radial-gradient(circle closest-side, #09f, transparent);
                    content: "";
                    transform: translate3d(-50%, -50%, 0);
                    transition: all 200ms ease;
                }
                &:hover::before {
                    --size: 400px;
                }
            }
            复制代码
            export default {
                name: "track-btn",
                methods: {
                    move(e) {
                        const x = e.pageX - e.target.offsetLeft;
                        const y = e.pageY - e.target.offsetTop;
                        e.target.style.setProperty("--x", `${x}px`);
                        e.target.style.setProperty("--y", `${y}px`);
                    }
                }
            };
            复制代码

            源码链接可在文章结尾处获取

            其实可结合鼠标事件来完成更多的酷炫效果,例如动画关联、事件响应等操作。没有做不到,只有想不到,尽情发挥你的想象力啦。

            之前在CodePen上还看到一个挺不错的栗子,一个悬浮视差按钮,具体代码涉及到一些3D变换的知识。看完源码后,按照其思路自己也实现一番,顺便对代码稍加改良并封装成Vue组件,存放到本课件示例代码中。感觉录制的GIF有点别扭,显示效果不太好,有兴趣的同学可下载本课件示例代码,自己运行看看效果。



            兼容

            对于现代浏览器来说,CSS变量的兼容性其实还是蛮好的,所以大家可放心使用。毕竟现在都是各大浏览器厂商快速迭代的时刻,产品对于用户体验来说是占了很大比重,因此在条件允许的情况下还是大胆尝新,不要被一些过去的所谓的规范所约束着。



            试问现在还有多少人愿意去维护IE6~IE9的兼容性,如果一个产品的用户体验受限于远古浏览器的压制(可能政务Web应用和金融Web应用除外吧),相信这个产品也不会走得很远。

            我们在完成一个产品的过程中,不仅仅是为了完成工作任务,如果在保证进度的同时能花点心思点缀一下,可能会有意外的收获。用心写好每一段代码,才是享受写代码的真谛

            总结

            本文通过循序渐进的方式探讨了CSS变量的运用和技巧,对于一个这么好用的特性,当然是不能放过啦。其实多多思考,就能把CSS变量用在很多场景上。笔者把本文提到的示例统一组成一个Demo,也方便有兴趣的同学通过课件示例代码进行学习,思考一些可能在阅读本文时没有注意到的细节。

            • Demo示例:条形加载条、心形加载条、标签导航栏、悬浮跟踪按钮、悬浮视差按钮
            • Demo地址:关注IQ前端,扫描文章底部二维码,后台回复变量,获取整套课件示例代码
            • Demo运行:里面的readme.html有详细说明,记得看喔

            写到最后,送给大家一个大大的彩蛋,一个暖心彩虹色调搭配的爱心点赞按钮。如果你觉得本文写得棒棒哒,请给笔者一个赞喔,就像下面那样。当然,彩蛋源码也在课件示例代码里啦。想了解更多的CSS开发技巧,可移步到笔者19年写的一篇9.2万阅读量的爆款文章《灵活运用CSS开发技巧(66个骚操作案例)》,保证满足你的眼球。



            结语

            ??关注+点赞+收藏+评论+转发??,原创不易,鼓励笔者创作更好的文章

  • 相关推荐

    如何设计一个优秀的电子商务产品详情页

    加入人人都是产品经理【起点学院】产品经理实战训练营,BAT产品总监手把手带你学产品电子商务网站的产品详情页面无疑是设计师和开发人员关注的最重要的网页之一。产品详情页面是客户作出“加入购物车”决定的页面...

    怎么在JS中使用Ajax进行异步请求?

    大家好,今天我来分享一项JavaScript的实战技巧,即如何在JS中使用Ajax进行异步请求,让你的网页速度瞬间提升。Ajax是一种在不刷新整个网页的情况下与服务器进行数据交互的技术,可以实现异步加...

    中小企业如何组建,管理团队_中小企业应当如何开展组织结构设计变革

    前言写了太多关于产品的东西觉得应该换换口味.从码农到架构师,从前端到平面再到UI、UE,最后走向了产品这条不归路,其实以前一直再给你们讲.产品经理跟项目经理区别没有特别大,两个岗位之间有很...

    前端监控 SDK 开发分享_前端监控系统 开源

    一、前言随着前端的发展和被重视,慢慢的行业内对于前端监控系统的重视程度也在增加。这里不对为什么需要监控再做解释。那我们先直接说说需求。对于中小型公司来说,可以直接使用三方的监控,比如自己搭建一套免费的...

    Ajax 会被 fetch 取代吗?Axios 怎么办?

    大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!今天给大家带来的主题是ajax、fetch...

    前端面试题《AJAX》_前端面试ajax考点汇总

    1.什么是ajax?ajax作用是什么?AJAX=异步JavaScript和XML。AJAX是一种用于创建快速动态网页的技术。通过在后台与服务器进行少量数据交换,AJAX可以使网页实...

    Ajax 详细介绍_ajax

    1、ajax是什么?asynchronousjavascriptandxml:异步的javascript和xml。ajax是用来改善用户体验的一种技术,其本质是利用浏览器内置的一个特殊的...

    6款可替代dreamweaver的工具_替代powerdesigner的工具

    dreamweaver对一个web前端工作者来说,再熟悉不过了,像我07年接触web前端开发就是用的dreamweaver,一直用到现在,身边的朋友有跟我推荐过各种更好用的可替代dreamweaver...

    我敢保证,全网没有再比这更详细的Java知识点总结了,送你啊

    接下来你看到的将是全网最详细的Java知识点总结,全文分为三大部分:Java基础、Java框架、Java+云数据小编将为大家仔细讲解每大部分里面的详细知识点,别眨眼,从小白到大佬、零基础到精通,你绝...

    福斯《死侍》发布新剧照 "小贱贱"韦德被改造前造型曝光

    时光网讯福斯出品的科幻片《死侍》今天发布新剧照,其中一张是较为罕见的死侍在被改造之前的剧照,其余两张剧照都是死侍在执行任务中的状态。据外媒推测,片方此时发布剧照,预计是为了给不久之后影片发布首款正式预...

    2021年超详细的java学习路线总结—纯干货分享

    本文整理了java开发的学习路线和相关的学习资源,非常适合零基础入门java的同学,希望大家在学习的时候,能够节省时间。纯干货,良心推荐!第一阶段:Java基础重点知识点:数据类型、核心语法、面向对象...

    不用海淘,真黑五来到你身边:亚马逊15件热卖爆款推荐!

    Fujifilm富士instaxMini8小黄人拍立得相机(黄色/蓝色)扫二维码进入购物页面黑五是入手一个轻巧可爱的拍立得相机的好时机,此款是mini8的小黄人特别版,除了颜色涂装成小黄人...

    2025 年 Python 爬虫四大前沿技术:从异步到 AI

    作为互联网大厂的后端Python爬虫开发,你是否也曾遇到过这些痛点:面对海量目标URL,单线程爬虫爬取一周还没完成任务;动态渲染的SPA页面,requests库返回的全是空白代码;好不容易...

    最贱超级英雄《死侍》来了!_死侍超燃

    死侍Deadpool(2016)导演:蒂姆·米勒编剧:略特·里斯/保罗·沃尼克主演:瑞恩·雷诺兹/莫蕾娜·巴卡林/吉娜·卡拉诺/艾德·斯克林/T·J·米勒类型:动作/...

    停止javascript的ajax请求,取消axios请求,取消reactfetch请求

    一、Ajax原生里可以通过XMLHttpRequest对象上的abort方法来中断ajax。注意abort方法不能阻止向服务器发送请求,只能停止当前ajax请求。停止javascript的ajax请求...