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

做者: JowayYoung
仓库: GithubCodePen
博客: 掘金思否知乎简书头条CSDN
公众号: IQ前端
联系我:关注公众号后有个人 微信
特别声明:原创不易,未经受权不得对此文章进行转载或抄袭,不然按侵权处理,如需转载或开通公众号白名单可联系我,但愿各位尊重原创的知识产权

前言

CSS变量又叫CSS自定义属性,为何会忽然提起这个不多人用到的东西呢?由于最近在重构我的官网,不知道为何忽然喜欢用上CSS变量,可能其自身隐藏的魅力,让笔者对它另眼相看。css

谈到为何会在CSS中使用变量,下面举个栗子,估计你们一看就会明白。html

/* 不使用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代码。git

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

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

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

可能有些同窗会问,Sass和Less早就实现了变量这个特性,何须再画蛇添足呢。但是细想一下,CSS变量对比Sass和Less的变量,又有它的过人之处。segmentfault

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

认识

原本打算用一半篇幅讲述CSS变量的规范和用法,可是网上一搜一大把就感受不必了,贴上阮一峰老师写的教程《CSS变量教程》。同时笔者也对CSS变量的细节地方进行一个整理,方便你们记忆。浏览器

  • 声明:--变量名
  • 读取:var(--变量名, 默认值)
  • 类型微信

    • 普通:只能用做属性值不能用做属性名
    • 字符:与字符串拼接 "Hello, "var(--name)
    • 数值:使用calc()与数值单位连用 var(--width) * 10px
  • 做用域frontend

    • 范围:在当前元素块做用域及其子元素块做用域下有效
    • 优先级别:内联样式 > ID选择器 > 类选择器 = 属性选择器 = 伪类选择器 > 标签选择器 = 伪元素选择器

接下来使用几个特别的场景展现CSS变量的魅力。仍是那句话,同样东西有使用的场景,那天然就会有它的价值,那么用的人也会愈来愈多。ide

使用场景

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

如下全部演示代码基于Vue文件,但HTML、CSS和JS分开书写,为了简化CSS的书写而使用Sass进行预处理,方便代码演示
条形加载条

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

strip-loading

<ul class="strip-loading flex-ct-x">
    <li v-for="v in 6" :key="v"></li>
</ul>
.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;
        }
    }
}

分析代码发现,每一个<li>只是存在animation-delay不一样,而其他代码则彻底相同,换成其余相似的List元素集合场景,那岂不是有10个<li>就写10个:nth-child

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

对于HTML部分的修改,让每一个<li>拥有一个本身做用域下的CSS变量。对于CSS部分的修改,就须要分析哪些属性是随着index递增而发生规律变化的,对规律变化的部分使用CSS变量表达式代替便可。

<ul class="strip-loading flex-ct-x">
    <li v-for="v in 6" :key="v" :style="`--line-index: ${v}`"></li>
</ul>
.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使每一个<li>拥有一个属于本身的做用域。例如第2个<li>--line-index的值为2,--time的计算值为200ms,换成第3个<li>后这两个值又会不一样了。

这就是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)进行修改了。

心形加载条

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

h-loading

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

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

<div class="heart-loading flex-ct-x">
    <ul style="--line-count: 9">
        <li v-for="v in 9" :key="v" :class="`line-${v}`" :style="`--line-index: ${v}`"></li>
    </ul>
</div>
.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;
    }
}
源码连接可在文章结尾处获取

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

heart-loading

标签导航栏

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

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

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

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

tab-navbar

<div class="tab-navbar">
    <nav>
        <a v-for="(v, i) in list" :key="v" :class="{ active: index === i }" @click="select(i)">标题{{i + 1}}</a>
    </nav>
    <div>
        <ul ref="tabs" :style="`--tab-count: ${list.length}`">
            <li v-for="(v, i) in list" :key="v" :style="`--bg-color: ${v}`">内容{{i + 1}}</li>
        </ul>
    </div>
</div>
.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);
        }
    }
};
源码连接可在文章结尾处获取

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

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

固然,这个标签导航栏也可经过纯CSS实现,有兴趣的同窗可看看笔者以前一篇文章里的纯CSS标签导航栏

悬浮跟踪按钮

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

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

track-btn

<a class="track-btn pr tac" @mousemove="move">
    <span>妙用CSS变量,让你的CSS变得更心动</span>
</a>
.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有点别扭,显示效果不太好,有兴趣的同窗可下载本课件示例代码,本身运行看看效果。

parallax-btn

兼容

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

caniuse-css-var

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

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

总结

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

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

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

like-btn

结语

欢迎在下方进行评论,喜欢本文的点个赞收个藏,同时也但愿各位朋友对文章里的要点进行补充或提出本身的看法。

关注IQ前端

关注公众号IQ前端,一个专一于CSS/JS开发技巧的前端公众号,更多前端小干货等着你喔

  • 关注后回复关键词免费领取视频教程
  • 关注后添加我微信拉你进技术交流群
  • 欢迎关注IQ前端,更多CSS/JS开发技巧只在公众号推送

相关文章
相关标签/搜索