妙用CSS變量,讓你的CSS變得更心動

做者:JowayYoung
倉庫:GithubCodePen
博客:官網掘金思否知乎
公衆號:IQ前端
特別聲明:原創不易,未經受權不得轉載或抄襲,如需轉載可聯繫筆者受權css

前言

CSS變量又叫CSS自定義屬性,爲何會忽然提起這個不多人用到的東西呢?由於最近在重構我的官網,不知道爲何忽然喜歡用上CSS變量,可能其自身隱藏的魅力,讓筆者對它另眼相看。html

談到爲何會在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文件。這樣每次新增需求都同時維護幾套主題顏色多麻煩啊。vue

此時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
  • 做用域
    • 範圍:在當前元素塊做用域及其子元素塊做用域下有效
    • 優先級別:內聯樣式 > ID選擇器 > 類選擇器 = 屬性選擇器 = 僞類選擇器 > 標籤選擇器 = 僞元素選擇器

接下來使用幾個特別的場景展現CSS變量的魅力。仍是那句話,同樣東西有使用的場景,那天然就會有它的價值,那麼用的人也會愈來愈多。markdown

使用場景

其實CSS變量有一個特別好用的場景,那就是結合List元素集合使用。若是不明白這是什麼,請繼續往下看。frontend

如下全部演示代碼基於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)進行修改了。

心形加載條

前段時間刷掘金看到陳大魚頭兄的心形加載條,以爲挺漂亮的,很帶感受。

heart-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前端,一個專一於CSS/JS開發技巧的前端公衆號,更多前端小乾貨等着你喔

  • 關注後回覆資料免費領取學習資料
  • 關注後回覆進羣拉你進技術交流羣
  • 歡迎關注IQ前端,更多CSS/JS開發技巧只在公衆號推送

相關文章
相關標籤/搜索