本篇文章帶大家了解一下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變量就派上用場了,提前跟設計小姐姐規(guī)范好各種需要變換的顏色并通過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早就實現(xiàn)了變量這個特性,何必再多此一舉呢。可是細想一下,CSS變量對比Sass和Less的變量,又有它的過人之處。
- 瀏覽器原生特性,無需經過任何轉譯就可直接運行
- DOM對象一員,極大便利了CSS與JS之間的聯(lián)系
認識
本來打算用一半篇幅講述CSS變量的規(guī)范和用法,但是網上一搜一大把就感覺沒必要了,貼上阮一峰老師寫的教程《CSS變量教程》。同時筆者也對CSS變量的細節(jié)地方進行一個整理,方便大家記憶。
- 聲明:
--變量名
- 讀?。?code>var(--變量名, 默認值)
- 類型
- 普通:只能用作
屬性值
不能用作屬性名
- 字符:與字符串拼接
"Hello, "var(--name)
- 數(shù)值:使用
calc()
與數(shù)值單位連用var(--width) * 10px
- 普通:只能用作
- 作用域
- 范圍:在
當前元素塊作用域
及其子元素塊作用域
下有效 - 優(yōu)先級別:
內聯(lián)樣式 > ID選擇器 > 類選擇器 = 屬性選擇器 = 偽類選擇器 > 標簽選擇器 = 偽元素選擇器
- 范圍:在
接下來使用幾個特別的場景展示CSS變量的魅力。還是那句話,一樣東西有使用的場景,那自然就會有它的價值,那么用的人也會越來越多。
使用場景
其實CSS變量有一個特別好用的場景,那就是結合List元素集合使用。如果不明白這是什么,請繼續(xù)往下看。
以下所有演示代碼基于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; } } }
分析代碼發(fā)現(xiàn),每個<li>
只是存在animation-delay
不同,而其余代碼則完全相同,換成其他類似的List元素集合場景,那豈不是有10個<li>
就寫10個:nth-child
。
顯然這種方法不靈活也不容易封裝成組件,如果能像JS那樣封裝成一個函數(shù),并根據參數(shù)輸出不同的樣式效果,那就更棒了。說到這里,很明顯就是為了鋪墊CSS變量的開發(fā)技巧了。
對于HTML部分的修改,讓每個<li>
擁有一個自己作用域下的CSS變量。對于CSS部分的修改,就需要分析哪些屬性是隨著index
遞增而發(fā)生規(guī)律變化的,對規(guī)律變化的部分使用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)
進行修改了。
心形加載條
前段時間刷掘金看到陳大魚頭兄
的心形加載條,覺得挺漂亮的,很帶感覺。
通過動圖分析,發(fā)現(xiàn)每條線條的背景色和動畫時延不一致,另外動畫運行時的高度也不一致。細心的你可能還會發(fā)現(xiàn),第1條和第9條的高度一致,第2條和第8條的高度一致,依次類推,得到高度變換相同類
的公式:對稱index = 總數(shù) + 1 - index
。
背景色使用了濾鏡的色相旋轉hue-rotate
函數(shù),目的是為了使顏色過渡得更加自然;動畫時延的設置和上面條形加載條
的設置一致。下面就用CSS變量根據看到的動圖實現(xiàn)一番。
<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中的運用以及一些妙用技巧,現(xiàn)在通過標簽導航欄演示CSS變量在JS中的運用。
JS中主要有3個操作CSS變量的API,看上去簡單易記,分別如下:
- 讀取變量:
elem.style.getPropertyValue()
- 設置變量:
elem.style.setProperty()
- 刪除變量:
elem.style.removeProperty()
先上效果圖,效果中主要是使用CSS變量標記每個Tab的背景色和切換Tab的顯示狀態(tài)。
<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
的值,就可實現(xiàn)不操作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實現(xiàn),有興趣的同學可看看筆者之前一篇文章里的純CSS標簽導航欄。
懸浮跟蹤按鈕
通過幾個栗子實踐了CSS變量在CSS和JS上的運用,相信大家已經掌握了其用法和技巧。之前在某個網站看過一個比較酷炫的鼠標懸浮特效,好像也是使用CSS變量實現(xiàn)的。筆者憑著記憶也使用CSS變量實現(xiàn)一番。
其實思路也比較簡單,先對按鈕進行布局和著色,然后使用偽元素標記鼠標的位置,定義--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`); } } };
其實可結合鼠標事件來完成