[譯] 關於 CSS 變數,你需要了解的一切

Colafornia發表於2018-03-25

[譯] 關於 CSS 變數,你需要了解的一切

本文是我新寫的電子書的第一章(電子書目前已支援 pdf 和 mobi 格式下載)。

大多數程式語言都支援變數。然而遺憾的是,CSS 從一開始就缺乏對原生變數的支援。

你寫 CSS 嗎?如果寫的話你就知道是沒法使用變數的。當然了,除非你使用像 Sass 這樣的前處理器。

像 Sass 這樣的前處理器是把變數的使用作為一大亮點。這是一個非常好的理由去嘗試使用這類前處理器。當然了,這個理由已然足夠好了。

Web 技術發展是非常快的,在此我很高興地報告 現在 CSS 支援變數了

然而前處理器還支援更多優秀特性,CSS 變數僅僅是其中之一。這些特性使得 Web 技術更加貼近未來。

這篇指南將向你展示變數是如何在原生 CSS 中工作的,以及怎樣使用變數讓你的程式設計工作更輕鬆。

你將學到

首先我將帶你粗略過一遍 CSS 變數的基礎知識。我相信任何理解 CSS 變數的嘗試都必須從這裡開始。

學習基礎知識是一件非常酷的事。更酷的是使用基礎知識來構建一個真正的應用。

因此,我將構建三個能夠體現 CSS 變數的使用及其易用性的專案,用這種方式把兩件事結合起來。下面是對這三個專案的快速預覽。

專案 1: 使用 CSS 變數建立一個有變化效果的元件

你可能已經構建過一個有變化效果的元件了。無論你是使用 React,Angular 還是 Vue,使用 CSS 變數都會讓構建過程更簡單。

[譯] 關於 CSS 變數,你需要了解的一切

使用 CSS 變數建立一個有變化效果的元件。

可以在 Codepen 上檢視這個專案。

專案 2: 使用 CSS 變數實現主題定製

可能你已經看過這個專案了。我會向你展示使用 CSS 變數來定製全站主題有多麼容易。

[譯] 關於 CSS 變數,你需要了解的一切

使用 CSS 變數定製全站主題。

可以在 Codepen 上檢視這個專案。

專案 3: 構建 CSS 變數展位

這是最後一個專案了,不要在意這個專案名,我想不出更好的名字了。

[譯] 關於 CSS 變數,你需要了解的一切

盒子的顏色是動態更新的。

請注意盒子的顏色是如何動態更新的,以及盒子容器是如何隨著輸入範圍值的變化進行 3D 旋轉的。

[譯] 關於 CSS 變數,你需要了解的一切
.

這個專案展示了使用 JavaScript 更新 CSS 變數的便利性,從中你還會嚐到響應式程式設計的甜頭。

這會是非常好玩的!

花點時間在 Codepen 上玩一玩。

注意:本文假定你對 CSS 已駕輕就熟。如果你對 CSS 掌握地不是很好,或者想學習如何創作出驚豔的 UI 效果,我建議你去學習我的 CSS 進階課程(共 85 課時的付費課程)。本文內容是該課程的一個節選。?

為何變數如此重要

如果你對前處理器和原生 CSS 中的變數並不熟悉的話,以下幾個原因可以為你解答為何變數如此重要。

原因 #1:使得程式碼更可讀

無需多言,你就可以判斷出,變數使得程式碼可讀性更好,更易於維護。

原因 #2:易於在大型文件中進行修改

如果把所有的常量都維護在一個單獨檔案中,想改動某一變數時就無需在上千行程式碼間來回跳轉進行修改。

這變得非常容易,僅僅在一個地方進行修改,就搞定了。

原因 #3:定位打字錯誤更快

在多行程式碼中定位錯誤非常痛苦,更痛苦的是錯誤是由打字錯誤造成的,它們非常難定位。善於使用變數可以免除這些麻煩。

至此,可讀性和可維護性是主要優點。

感謝 CSS 變數,現在我們在原生 CSS 中也能享受到以上這些優點了。

定義 CSS 變數

先以你已經很熟悉的東西開始:JavaScript 中的變數。

JavaScript 中,一個簡單的變數宣告會像這樣:

var amAwesome;
複製程式碼

然後你像這樣可以賦值給它:

amAwesome = "awesome string"
複製程式碼

在 CSS 中,以兩個橫線開頭的“屬性”都是 CSS 變數。

/*你可以找到變數嗎? */
.block {
 color: #8cacea;
--color: blue
}
複製程式碼

[譯] 關於 CSS 變數,你需要了解的一切

CSS 變數也被稱為“自定義屬性”。

CSS 變數作用域

還有一點需要注意。

請記住 JavaScript 中變數是有作用域的,要麼是全域性作用域,要麼就是區域性作用域

CSS 變數也是如此。

思考一下下面這個例子:

:root {
  --main-color: red
}
複製程式碼

:root 選擇器允許你定位到 DOM 中的最頂級元素或文件樹。

所以,這種方式宣告的變數就屬於具有全域性作用域的變數。

明白了嗎?

[譯] 關於 CSS 變數,你需要了解的一切

區域性變數與全域性變數。

示例 1

假設你想建立一個 CSS 變數來儲存站點的主題顏色。

你會怎麼做呢?

  1. 建立一個作用域選擇器。通過 :root 建立一個全域性變數。
:root {

}
複製程式碼
  1. 定義變數
:root {
 --primary-color: red
}
複製程式碼

請記住,在 CSS 中,以兩個橫線開頭的“屬性”都是 CSS 變數,比如 --color

就是這麼簡單。

使用 CSS 變數

變數一旦被定義並賦值,你就可以在屬性值內使用它了。

但是有個小問題。

如果你用過前處理器的話,一定已經習慣通過引用變數名來使用該變數了。比如:

$font-size: 20px

.test {
  font-size: $font-size
}
複製程式碼

原生 CSS 變數有些不同,你需要通過 var() 函式來引用變數。

在上面這個例子中,使用 CSS 變數就應該改成這樣:

:root {
  --font-size: 20px
}

.test {
  font-size: var(--font-size)
}
複製程式碼

兩種寫法大不一樣。

[譯] 關於 CSS 變數,你需要了解的一切

請記得使用 var 函式。

一旦你習慣了這種方式,就會愛上 CSS 變數的。

另一個重要的注意事項是,在 Sass 這類前處理器中,你可以在任意地方使用變數,做各種計算,但是需要注意,在原生 CSS 中,你只能將變數設定為屬性值。

/*這是錯的*/
.margin {
--side: margin-top;
var(--side): 20px;
}
複製程式碼

[譯] 關於 CSS 變數,你需要了解的一切

由於屬性名非法,這段宣告會丟擲語法錯誤

CSS 變數也不能做數學計算。如果需要的話,可以通過 CSS 的 calc() 函式進行計算。接下來我們會通過示例來闡述。

/*這是錯的*/
.margin {
--space: 20px * 2;
font-size:  var(--space);  // 並非 40px
}
複製程式碼

如果你必須要做數學計算的話,可以像這樣使用 calc() 函式:

.margin {
--space: calc(20px * 2);
font-size:  var(--space);  /*等於 40px*/
}
複製程式碼

關於屬性的一些事

以下是幾個需要闡述的屬性行為:

1. 自定義屬性就是普通屬性,可以在任意元素上宣告自定義屬性

在 p,section,aside,root 元素,甚至偽元素上宣告自定義屬性,都可以執行良好。

[譯] 關於 CSS 變數,你需要了解的一切

這些自定義屬性工作時與普通屬性無異。

2. CSS 變數由普通的繼承與級聯規則解析

請思考以下程式碼:

div {
  --color: red;
}

div.test {
  color: var(--color)
}

div.ew {
  color: var(--color)
}
複製程式碼

像普通變數一樣,--color 的值會被 div 元素們繼承。

[譯] 關於 CSS 變數,你需要了解的一切

3. CSS 變數可以通過 @media 和其它條件規則變成條件式變數

和其它屬性一樣,你可以通過 @media 程式碼塊或者其它條件規則改變 CSS 變數的值。

舉個例子,以下程式碼會在大屏裝置下改變變數 gutter 的值。

:root {
 --gutter: 10px
}

@media screen and (min-width: 768px) {
    --gutter: 30px
}
複製程式碼

[譯] 關於 CSS 變數,你需要了解的一切

對於響應式設計很有用。

4. HTML 的 style 屬性中可以使用 CSS 變數。

你可以在行內樣式中設定變數值,變數依然會如期執行。

<!--HTML-->
<html style="--color: red">

<!--CSS-->
body {
  color: var(--color)
}
複製程式碼

[譯] 關於 CSS 變數,你需要了解的一切

行內設定變數值。

要注意這一點,CSS 變數是區分大小寫的。我為了減小壓力,選擇都採用小寫形式,這件事見仁見智。

/*這是兩個不同的變數*/
:root {
 --color: blue;
--COLOR: red;
}
複製程式碼

解析多重宣告

與其它屬性相同,多重宣告會按照標準的級聯規則解析。

舉個例子:

/*定義變數*/
:root { --color: blue; }
div { --color: green; }
#alert { --color: red; }

/*使用變數*/
* { color: var(--color); }
複製程式碼

根據以上的變數宣告,下列元素是什麼顏色?

<p>What's my color?</p>
<div>and me?</div>
<div id='alert'>
  What's my color too?
  <p>color?</p>
</div>
複製程式碼

你想出答案了嗎?

第一個 p 元素顏色是 藍色p 選擇器上並沒有直接的顏色定義,所以它從 :root 上繼承屬性值

:root { --color: blue; }
複製程式碼

第一個 div 元素顏色是 綠色。這個很簡單,因為有變數直接定義在 div 元素上

div { --color: green; }
複製程式碼

具有 ID 為 alertdiv 元素顏色不是綠色,而是 紅色

#alert { --color: red; }
複製程式碼

由於有變數作用域直接是在這個 ID 上,變數所定義的值會覆蓋掉其它值。#alert 選擇器是一個更為特定的選擇器。

最後,#alert 元素內的 p 元素顏色是 紅色

這個 p 元素上並沒有變數宣告。由於 :root 宣告的顏色屬性是 藍色,你可能會以為這個 p 元素的顏色也是 藍色

:root { --color: blue; }
複製程式碼

如其它屬性一樣, CSS 變數是會繼承的,因此 p 元素的顏色值繼承自它的父元素 #alert

#alert { --color: red; }
複製程式碼

[譯] 關於 CSS 變數,你需要了解的一切

小測驗的答案。

解決迴圈依賴

迴圈依賴會出現在以下幾個場景中:

  1. 當一個變數依賴自己本身時,也就是說這個變數通過 var() 函式指向自己時。
:root {
  --m: var(--m)
}

body {
  margin: var(--m)
}
複製程式碼
  1. 兩個以上的變數互相引用。
:root {
  --one: calc(var(--two) + 10px);
  --two: calc(var(--one) - 10px);
}
複製程式碼

請注意不要在你的程式碼中引入迴圈依賴。

使用非法變數會怎樣?

語法錯誤機制已被廢棄,非法的 var() 會被預設替換成屬性的初始值或繼承的值。

思考一下下面這個例子:

:root { --color: 20px; }
p { background-color: red; }
p { background-color: var(--color); }
複製程式碼

[譯] 關於 CSS 變數,你需要了解的一切

正如我們所料,--color 變數會在 var() 中被替換,但是替換後,屬性值 background-color: 20px 是非法的。由於 background-color 不是可繼承的屬性,屬性值將預設被替換成它的初始值即 transparent

[譯] 關於 CSS 變數,你需要了解的一切

注意,如果你沒有通過變數替換,而是直接寫 background-color: 20px 的話,這個背景屬性宣告就是非法的,則使用之前的宣告定義。

[譯] 關於 CSS 變數,你需要了解的一切

當你自己寫宣告時,情況就不一樣了。

使用單獨符號時要小心

當你用下面這種方式來設定屬性值時,20px 則會按照單獨符號來解析。

font-size: 20px
複製程式碼

有一個簡單的方法去理解,20px 這個值可以看作是一個單獨的 “實體”。

在使用 CSS 變數構建單獨符號時需要非常小心。

舉個例子,思考以下程式碼:

:root {
 --size: 20
}

div {
  font-size: var(--size)px /*這是錯的*/
}
複製程式碼

可能你會以為 font-size 的值是 20px,那你就錯了。

瀏覽器的解釋結果是 20 px

請注意 20 後面的空格

因此,如果你必須建立單獨符號的話,請用變數來代表整個符號。比如 --size: 20px,或者使用 calc 函式比如 calc(var(--size) * 1px) 中的 --size 就是等於 20

如果你沒看懂的話也不用擔心,在下個示例中我會解釋地更詳細。

一顆賽艇!

現在我們已經到了期待已久的章節了。

我將通過構建幾個有用的小專案,在實際應用中引導你瞭解之前所學的理論。

讓我們開始吧。

專案 1: 使用 CSS 變數建立一個有變化效果的元件

思考一下需要構建兩個不同按鈕的場景,兩個按鈕的基本樣式相同,只有些許不同。

[譯] 關於 CSS 變數,你需要了解的一切

這個場景中,按鈕的 background-colorborder-color 屬性不同。

那麼你會怎麼做呢?

這裡有一個典型解決方案。

建立一個叫 .btn 的基礎類,然後加上用於變化的類。舉個例子:

<button class="btn">Hello</button>
<button class="btn red">Hello</button>
複製程式碼

.btn 包括了按鈕上的基礎樣式,如:

.btn {
  padding: 2rem 4rem;
  border: 2px solid black;
  background: transparent;
  font-size: 0.6em;
  border-radius: 2px;
}

/*hover 狀態下*/
.btn:hover {
  cursor: pointer;
  background: black;
  color: white;
}
複製程式碼

在哪裡引入變化量呢?

這裡:

/* 變化 */

.btn.red {
  border-color: red
}
.btn.red:hover {
  background: red
}
複製程式碼

你看到我們將程式碼複製到好幾處麼?這還不錯,但是我們可以用 CSS 變數來做的更好。

第一步是什麼?

用 CSS 變數替代變化的顏色,別忘了給變數加上預設值。

.btn {
   padding: 2rem 4rem;
   border: 2px solid var(--color, black);
   background: transparent;
   font-size: 0.6em;
   border-radius: 2px;
 }

 /*hover 狀態下*/
 .btn:hover {
  cursor: pointer;
   background: var(--color, black);
   color: white;
 }
複製程式碼

當你寫下 background: **var(--color, black)** 時,就是將背景色的值設定為變數 --color 的值,如果變數不存在的話則使用預設值 **black**

這就是設定變數預設值的方法,與在 JavaScript 和其它語言中的做法一樣。

這是使用變數的好處。

使用了變化量,就可以用下面這種方法來應用變數的新值:

.btn.red {
   --color: red
 }
複製程式碼

就是這麼簡單。現在當使用 .red 類時,瀏覽器注意到不同的 --color 變數值,就會立即更新按鈕的樣式了。

如果你要花很多時間來構建可複用元件的話,使用 CSS 變數是一個非常好的選擇。

這是並排比較:

[譯] 關於 CSS 變數,你需要了解的一切

不用 CSS 變數 VS 使用 CSS 變數。

如果你有非常多的可變選項的話,使用 CSS 變數還會為你節省很多打字時間。

[譯] 關於 CSS 變數,你需要了解的一切

看出不同了嗎??

專案 2: 使用 CSS 變數實現主題定製

我很確定你之前一定遇到過主題定製的需求。支援主題定製的站點讓使用者有了自定義的體驗,感覺站點在自己的掌控之中。

下面是我寫的一個簡單示例:

[譯] 關於 CSS 變數,你需要了解的一切

使用 CSS 變數來實現有多麼容易呢?

我們來看看。

在此之前,我想提醒你,這個示例非常重要。通過這個示例我將引導你理解使用 JavaScript 更新 CSS 變數的思想。

非常好玩!

你會愛上它的!

我們究竟想做什麼。

CSS 變數的美在於其本質是響應式的。一旦 CSS 變數更新了,任意帶有 CSS 變數的屬性的值也都會隨之更新。

從概念上講,下面這張圖解釋了這個示例的流程。

[譯] 關於 CSS 變數,你需要了解的一切

流程。

因此,我們需要給點選事件監聽器寫一些 JavaScript 程式碼。

在這個簡單的示例裡,文字與頁面的顏色和背景色都是基於 CSS 變數的。

當你點選頁面上方的按鈕時,JavaScript 會將 CSS 變數中的顏色切換成別的顏色,頁面的背景色也就隨之更新。

這就是全部了。

還有一件事。

當我說 CSS 變數切換成別的顏色時,是怎麼做到的呢?

[譯] 關於 CSS 變數,你需要了解的一切

行內設定變數。

即使是在行內設定,CSS 變數也會生效。在 JavaScript 中,我們控制了文件的根節點,然後就可以在行內給 CSS 變數設定新的值了。

明白了嗎?

我們說了太多了,現在該幹些實際的了。

結構初始化

初始化結構是這樣的:

<div class="theme">
  <button value="dark">dark</button>
  <button value="calm">calm</button>
  <button value="light">light</button>
</div>

<article>
...
</article>
複製程式碼

結構中有三個父元素為 .theme 的按鈕元素。為了看起來儘可能簡短,我將 article 元素內的內容截斷了。article 元素內就是頁面的內容。

設定頁面樣式

專案的成功始於頁面的樣式。這個技巧非常簡單。

我們設定頁面樣式的 background-colorcolor 是基於變數的,而不是寫死的屬性值。

這就是我說的:

body {
  background-color: var(--bg, white);
  color: var(--bg-text, black)
}
複製程式碼

這麼做的原因顯而易見。無論何時按鈕被點選,我們都會改變文件中兩個變數的值。

根據變數值的改變,頁面的整體樣式也就隨之更新。小菜一碟。

[譯] 關於 CSS 變數,你需要了解的一切

讓我們繼續前進,解決在 JavaScript 中更新屬性值的問題。

進入 JavaScript

我將直接把這個專案所需的全部 JavaScript 展示出來。

const root = document.documentElement
const themeBtns = document.querySelectorAll('.theme > button')

themeBtns.forEach((btn) => {
  btn.addEventListener('click', handleThemeUpdate)
})

function handleThemeUpdate(e) {
  switch(e.target.value) {
    case 'dark':
      root.style.setProperty('--bg', 'black')
      root.style.setProperty('--bg-text', 'white')
      break
    case 'calm':
       root.style.setProperty('--bg', '#B3E5FC')
       root.style.setProperty('--bg-text', '#37474F')
      break
    case 'light':
      root.style.setProperty('--bg', 'white')
      root.style.setProperty('--bg-text', 'black')
      break
  }
}
複製程式碼

不要被這段程式碼嚇到,它比你想象的要簡單。

首先,儲存一份對根節點的引用, const root = document.documentElement

這裡的根節點就是 HTML 元素。你很快就會明白為什麼這很重要。如果你很好奇的話,我可以先告訴你一點,給 CSS 變數設定新值時需要根節點。

同樣地,儲存一份對按鈕的引用, const themeBtns = document.querySelectorAll('.theme > button')

querySelectorAll 生成的資料是可以進行遍歷的類陣列結構。遍歷按鈕,然後給按鈕設定點選事件監聽。

這裡是怎麼做:

themeBtns.forEach((btn) => {
  btn.addEventListener('click', handleThemeUpdate)
})
複製程式碼

handleThemeUpdate 函式去哪了?我們接下來就會討論這個函式。

每個按鈕被點選後,都會呼叫回撥函式 handleThemeUpdate。因此知道是哪個按鈕被點選以及後續該執行什麼正確操作很重要。

鑑於此,我們使用了 switch 操作符,基於被點選的按鈕的值來執行不同的操作。

接下來再看一遍這段 JavaScript 程式碼,你會理解地更好一些。

專案 3: 構建 CSS 變數展位

避免你錯過它,這是我們即將構建的專案:

[譯] 關於 CSS 變數,你需要了解的一切

請記住盒子的顏色是動態更新的,以及盒子容器是隨著輸入範圍值的變化進行 3D 旋轉的。

[譯] 關於 CSS 變數,你需要了解的一切

你可以直接在 Codepen 上玩一下這個專案。

這是使用 JavaScript 更新 CSS 變數以及隨之而來的響應式特性的絕佳示例。

讓我們來看看如何來構建。

結構

以下是所需的元件。

  1. 一個範圍輸入框
  2. 一個裝載使用說明文字的容器
  3. 一個裝載盒子列表的 section,每個盒子包含輸入框

[譯] 關於 CSS 變數,你需要了解的一切

結構變得很簡單。

以下就是:

<main class="booth">
  <aside class="slider">
    <label>Move this ? </label>
    <input class="booth-slider" type="range" min="-50" max="50" value="-50" step="5"/>
  </aside>

  <section class="color-boxes">
    <div class="color-box" id="1"><input value="red"/></div>
    <div class="color-box" id="2"><input/></div>
    <div class="color-box" id="3"><input/></div>
    <div class="color-box" id="4"><input/></div>
    <div class="color-box" id="5"><input/></div>
    <div class="color-box" id="6"><input/></div>
  </section>

  <footer class="instructions">
    ?? Move the slider<br/>
    ?? Write any color in the red boxes
  </footer>
</main>
複製程式碼

以下幾件事需要注意。

  1. 範圍輸入代表了從 -5050 範圍的值,step 值為 5。因此範圍輸入的最小值就是 -50
  2. 如果你並不確定範圍輸入是否可以執行,可以在 w3schools 上檢查以下
  3. 注意類名為 .color-boxes 的 section 是如何包含其它 .color-box 容器的。這些容器中包含輸入框。
  4. 第一個輸入框有預設值為 red。

理解了文件結構後,給它新增樣式:

[譯] 關於 CSS 變數,你需要了解的一切

  1. .slider.instructions 設定為脫離文件流,將它們的 position 設定為 absolute
  2. body 元素的背景色設定為日出的顏色,並在左下角用花朵作裝飾
  3. color-boxes 容器定位到中間
  4. color-boxes 容器新增樣式

讓我們把這些任務都完成。

以下程式碼會完成第一步。

/* Slider */
.slider,
.instructions {
  position: absolute;
  background: rgba(0,0,0,0.4);
  padding: 1rem 2rem;
  border-radius: 5px
}
.slider {
  right: 10px;
  top: 10px;
}
.slider > * {
  display: block;
}


/* Instructions */
.instructions {
  text-align: center;
  bottom: 0;
  background: initial;
  color: black;
}
複製程式碼

這段程式碼並不像你想的那般複雜。希望你能通讀一遍並能讀懂,如果沒有的話,可以留下評論或者發個 twitter。

body 新增樣式會涉及到更多內容,希望你足夠了解 CSS。

既然我們想用背景顏色和背景圖來設定元素的樣式,那麼使用 background 簡寫屬性設定多個背景屬性可能是最佳選擇。

就是這樣的:

body {
  margin: 0;
  color: rgba(255,255,255,0.9);
  background: url('http://bit.ly/2FiPrRA') 0 100%/340px no-repeat, var(--primary-color);
  font-family: 'Shadows Into Light Two', cursive;
}
複製程式碼

url 是向日葵圖片的連結。

接下來設定的 0 100% 代表圖片在背景中的位置。

這個插圖展示了 CSS 的 background position 屬性是如何工作的:

[譯] 關於 CSS 變數,你需要了解的一切

來自於: CSS 進階指南

[譯] 關於 CSS 變數,你需要了解的一切

來自於: CSS 進階指南

正斜槓後面的代表 background-size 被設定為 340px,如果將它設定得更小的話,圖片也會變得更小。

no-repeat,你可能已經猜到它是做什麼的。它避免背景圖片自我複製,鋪滿背景。

最後,跟在逗號後面的是第二個背景屬性宣告。這一次,我們僅僅將 background-color 設定為 var(primary-color)

哇,這是個變數。

這意味著你必須定義變數。 就是這樣:

:root {
  --primary-color: rgba(241,196,15 ,1)
}
複製程式碼

這裡講主題色設定為日出黃。沒什麼大問題。馬上,我們就會在這裡設定更多的變數。

現在,我們將 color-boxes 定位到中間

main.booth {
  min-height: 100vh;

  display: flex;
  justify-content: center;
  align-items: center;
}
複製程式碼

主容器充當 flex 容器,它的子元素會正確地被定位到頁面中間。也就是說我們的 color-box 容器會被定位到頁面中間。

我們把 color-boxes 以及它的子元素容器變得更好看一些。

首先,是子元素:

.color-box {
  padding: 1rem 3.5rem;
  margin-bottom: 0.5rem;
  border: 1px solid rgba(255,255,255,0.2);
  border-radius: 0.3rem;
  box-shadow: 10px 10px 30px rgba(0,0,0,0.4);
}
複製程式碼

這就加上了好看的陰影,使得效果更酷炫了。

還沒結束,我們給整體的 container-boxes 容器加上樣式:

/* Color Boxes */
.color-boxes {
  background: var(--secondary-color);
  box-shadow: 10px 10px 30px rgba(0,0,0,0.4);
  border-radius: 0.3rem;

  transform: perspective(500px) rotateY( calc(var(--slider) * 1deg));
  transition: transform 0.3s
}
複製程式碼

哇!

變得太複雜了。

去掉一些。

變得簡單點:

.color-boxes {
   background: var(--secondary-color);
   box-shadow: 10px 10px 30px rgba(0,0,0,0.4);
   border-radius: 0.3rem;
}
複製程式碼

你知道效果會變成什麼樣,對吧?

這裡有個新變數,需要在根元素中宣告新增進來。

:root {
  --primary-color: rgba(241,196,15 ,1);
  --secondary-color: red;
}
複製程式碼

第二個顏色是紅色,我們會給容器加上紅色的背景。

接下來這部分可能會讓你覺得難以理解:

/* Color Boxes */
.color-boxes {
  transform: perspective(500px) rotateY( calc(var(--slider) * 1deg));
  transition: transform 0.3s
}
複製程式碼

又是我們會將 transform 的屬性值簡寫成上面這樣。

[譯] 關於 CSS 變數,你需要了解的一切

舉個例子:

transform: perspective(500px) rotateY( 30deg);
複製程式碼

這個 transform 簡寫用了兩個不同的函式。一個是視角,另一個是沿著 Y 軸旋轉。

那麼 perspective 函式 和 rotateY 函式是做什麼的呢?

perspective() 函式應用於 3D 空間內旋轉的元素。它啟用了三維空間,並沿 z 軸給出元素的深度。

可以在 codrops 上閱讀更多有關 perspective 的知識。

rotateY 函式是幹什麼的?

啟用三維空間後,元素具有了 x,y,z 軸。rotateY 就是元素圍繞 Y 平面旋轉。

下面這個 codrops 的圖對於視覺化理解很有幫助。

[譯] 關於 CSS 變數,你需要了解的一切

Codrops

我希望這能讓你更明白一些。

回到之前的話題。

[譯] 關於 CSS 變數,你需要了解的一切

當你回到這裡,你知道哪個函式影響 .container-box 的旋轉了嗎?

是 rotateY 函式使得盒子沿著 Y 周旋轉。

由於傳入 rotateY 函式的值將被 JavaScript 更新,這個值也將通過變數來傳入。

[譯] 關於 CSS 變數,你需要了解的一切

為什麼要給變數乘上 1deg?

作為一般的經驗法則,為了顯式地更靈活,建議在構建單獨符號時變數中儲存沒有單位的值。

通過 calc 函式,你可以用乘法將它們轉化成任何單位。

[譯] 關於 CSS 變數,你需要了解的一切

這意味著你可以為所欲為。將作為比例的 deg 轉換為視窗單位 vw 也可以。

在這個場景中,我們通過 “數字” 乘上 1deg 將數字轉換成角度

[譯] 關於 CSS 變數,你需要了解的一切

由於 CSS 不懂數學,你需要將公式傳入 calc 函式,這樣 CSS 才能正確計算。

完成之後我們就可以繼續了。我們可以在 JavaScript 中用各種方法來更新它。

現在,只剩下一點點的 CSS 程式碼需要寫了。

就是這些:

/* 給每個盒子新增顏色 */
.color-box:nth-child(1) {
  background: var(--bg-1)
}
.color-box:nth-child(2) {
  background: var(--bg-2)
}
.color-box:nth-child(3) {
  background: var(--bg-3)
}
.color-box:nth-child(4) {
  background: var(--bg-4)
}
.color-box:nth-child(5) {
  background: var(--bg-5)
}
.color-box:nth-child(6) {
  background: var(--bg-6)
}
複製程式碼

這些奇怪的東西是什麼?

首先,nth-child 選擇器用來選擇子盒子。

[譯] 關於 CSS 變數,你需要了解的一切

這裡需要一些前瞻。我們知道,每個盒子的背景色都會更新。我們也知道背景色需要用變數表示,以在 JavaScript 中更新。對吧?

接下來:

.color-box:nth-child(1) {
  background: var(--bg-1)
}
複製程式碼

簡單吧。

這裡有個問題。如果變數不存在的話怎麼辦?

我們一個回退方式。

這是可行的:

.color-box:nth-child(1) {
  background: var(--bg-1, red)
}
複製程式碼

在這個特殊例項中,我選擇不提供任何回退方式。

如果某個屬性值中使用的變數非法,屬性將使用其初始值。

因此,當 --bg-1 非法或者不可用時,背景色會預設切換成它的初始顏色或者透明。

初始值指向屬性還未顯式設定時的值。比如說,如果你沒有給元素設定 background-color 屬性的話,它的背景色會預設為 transparent

初始值是一種預設屬性值。

寫點 JavaScript

在 JavaScript 這一邊需要做的事情很少。

首先要處理一下 slider。

僅僅五行程式碼就可以!

const root = document.documentElement
const range = document.querySelector('.booth-slider')

// 一旦 slider 的範圍值發生變化,就執行回撥
range.addEventListener('input', handleSlider)

function handleSlider (e) {
  let value = e.target.value
  root.style.setProperty('--slider', value)
}
複製程式碼

這很簡單,對吧?

我來解釋一下。

首先,儲存一份 slider 元素的引用,const range = document.querySelector('.booth-slider')

設定一個事件監聽器,一旦範圍輸入值發生變化就會觸發,range.addEventListener('input', handleSlider)

寫一個回撥函式, handleSlider

function handleSlider (e) {
  let value = e.target.value
  root.style.setProperty('--slider', value)
}
複製程式碼

[譯] 關於 CSS 變數,你需要了解的一切

root.style.setProperty('--slider', value) 的意思是獲取 root 元素(HTML),讀取它的樣式,並給它設定屬性。

處理顏色變化

這與處理 slider 值的變化一樣簡單。

這麼做就可以:

const inputs = document.querySelectorAll('.color-box > input')

// 一旦輸入值發生變化,執行回撥
inputs.forEach(input => {
  input.addEventListener('input', handleInputChange)
})

function handleInputChange (e) {
  let value = e.target.value
  let inputId = e.target.parentNode.id
  let inputBg = `--bg-${inputId}`
  root.style.setProperty(inputBg, value)
}
複製程式碼

儲存一份所有文字輸入的引用, const inputs = document.querySelectorAll('.color-box > input')

給每個輸入框加上事件監聽:

inputs.forEach(input => {
   input.addEventListener('input', handleInputChange)
})
複製程式碼

handleInputChange 函式:

function handleInputChange (e) {
  let value = e.target.value
  let inputId = e.target.parentNode.id
  let inputBg = `--bg-${inputId}`
  root.style.setProperty(inputBg, value)
}
複製程式碼

[譯] 關於 CSS 變數,你需要了解的一切

嗯……

就是這些!

專案完成了。

我遺漏了什麼?

當我完成並修改了初稿後才發現我沒有提到瀏覽器支援。那讓我來處理這個爛攤子。

對於 CSS 變數的(又名自定義屬性)瀏覽器支援並不差。 瀏覽器支援性非常好,基本所有的現代瀏覽器都支援良好(本文寫作時已超過 87%)。

[譯] 關於 CSS 變數,你需要了解的一切

caniuse

那麼,你可以在生產環境使用 CSS 變數嗎?當然可以!但是這多大程度上適用於你還需自己判斷。

好的一面是,你可以使用像 Myth 這樣的前處理器來使用 CSS 變數。它將“未來的” CSS 預編譯成現在你就可以使用的程式碼,是不是很贊?

如果你有使用 postCSS 的經驗, 這也同樣是一個好方法。這是 postCSS 的 CSS 變數模組

就這些,我已全部寫完。

不好,我遇到了問題!

[譯] 關於 CSS 變數,你需要了解的一切

購買電子書 可以線上閱讀, 還能獲得 私人的 slack 邀請,你可以向我諮詢任何問題。

這是個公平交易,對吧?

稍後聯絡! ?


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章