這篇文章帶大家了解CSS變量,介紹一下CSS變數的用法,看看如何巧用CSS變量,讓你的CSS變得更心動,讓你的專案更加酷炫!
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使用變數的好處:
可能有些同學會問, Sass和Less早就實現了變數這個特性,何必再多此一舉呢。可是細想一下,CSS變數比較Sass和Less的變量,又有它的過人之處。
本來打算用一半篇幅講述CSS變數的規格和用法,但是網路一搜一大把就感覺沒必要了,貼上阮一峰老師寫的教程《CSS變數教程》。同時筆者也對CSS變數的細節地方進行一個整理,方便大家記憶。
--變數名稱
<li>讀取:var(--變數名稱, 預設值)
<li>類型屬性值
不能用作屬性名稱
<li>#字元:與字串拼接" Hello, "var(--name)
<li>數值:使用calc()
與數值單位連用var(--width) * 10px
目前元素區塊作用域
及其子元素區塊作用域
下有效 <li>優先等級:內嵌樣式> ID選擇器> 類別選擇器= 屬性選擇器= 偽類選擇器> 標籤選擇器= 偽元素選擇器
#接下來使用幾個特別的場景來展示CSS變數的魅力。還是那句話,一樣東西有使用的場景,那自然就會有它的價值,那麼用的人也會越來越多。
其實CSS變數有一個特別好用的場景,那就是結合List元素集合使用。如果你不明白這是什麼,請繼續往下看。
以下所有演示代码基于vue文件,但HTML、CSS和JS分开书写,为了简化CSS的书写而使用Sass进行预处理,方便代码演示
條形載入條
一個條形載入條通常由幾個線條組成,並且每個線條對應一個存在不同時延的相同動畫,透過時間差運行相同的動畫,從而產生載入效果。估計大部分的同學可能會把CSS程式碼寫成以下這樣。
<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)
进行修改了。
心形加载条
前段时间刷掘金看到陈大鱼头兄
的心形加载条,觉得挺漂亮的,很带感觉。
通过动图分析,发现每条线条的背景色和动画时延不一致,另外动画运行时的高度也不一致。细心的你可能还会发现,第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; } }
一波操作后就有了下面的效果。和陈大鱼头兄
的心形加载条对比一下,颜色、波动曲线和跳动频率有点不一样,在暖色调的蔓延和肾上腺素的飙升下,这是一种心动的感觉。想起自己曾经写的一首诗:我见犹怜,爱不释手,雅俗共赏,君子好逑
。
标签导航栏
上面通过两个加载条演示了CSS变量在CSS中的运用以及一些妙用技巧,现在通过标签导航栏演示CSS变量在JS中的运用。
JS中主要有3个操作CSS变量的API,看上去简单易记,分别如下:
elem.style.getPropertyValue()
<li>设置变量:elem.style.setProperty()
<li>删除变量:elem.style.removeProperty()
先上效果图,效果中主要是使用CSS变量标记每个Tab的背景色和切换Tab的显示状态。
<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></ul>
上定义--tab-index
表示Tab当前的索引,当点击按钮时重置--tab-index
的值,就可实现不操作DOM来移动<ul></ul>
的位置显示指定的Tab。不操作DOM而可移动<ul></ul>
是因为定义了--tab-move
,通过calc()
计算--tab-index
与--tab-move
的关系,从而操控transform: translate3d()
来移动<ul></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获取鼠标在按钮上的offsetLeft
和offsetLeft
分别赋值给--x
和--y
,再对伪元素添加径向渐变的背景色,大功告成,一个酷炫的鼠标悬浮跟踪特效就这样诞生了。
<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有点别扭,显示效果不太好,有兴趣的同学可下载本课件示例代码,自己运行看看效果。
对于现代浏览器来说,CSS变量的兼容性其实还是蛮好的,所以大家可放心使用。毕竟现在都是各大浏览器厂商快速迭代的时刻,产品对于用户体验来说是占了很大比重,因此在条件允许的情况下还是大胆尝新,不要被一些过去的所谓的规范所约束着。
试问现在还有多少人愿意去维护IE6~IE9的兼容性,如果一个产品的用户体验受限于远古浏览器的压制(可能政务Web应用和金融Web应用除外吧
),相信这个产品也不会走得很远。
我们在完成一个产品的过程中,不仅仅是为了完成工作任务,如果在保证进度的同时能花点心思点缀一下,可能会有意外的收获。用心写好每一段代码,才是享受写代码的真谛。
本文通过循序渐进的方式探讨了CSS变量的运用和技巧,对于一个这么好用的特性,当然是不能放过啦。其实多多思考,就能把CSS变量用在很多场景上。
更多编程相关知识,请访问:编程入门!!
以上是巧用CSS變量,讓你的專案更加酷!的詳細內容。更多資訊請關注PHP中文網其他相關文章!