單行 CSS3 variable 操作 實現邏輯至視圖層單向綁定

當我們將複雜的動畫、過場行為完全交託給 CSS 之後,JavaScript 只需在頂層設定其使用的策略與變數,CSS 即可自行產生對應規格的動畫,而非透過 JavaScript 來模擬動畫、HTML 改變的硬核,避免效能的 Impact 。

realdennis
4 min readOct 10, 2019

寫在文前

讓我們在文章開始之前,一起來想像如左側的效果,完全不透過模板語言去綁定資料。

需要撰寫多少行的 JavaScript 程式碼呢?

非關:註冊事件、產生隨機數

有關:更新進度的文字(操作DOM innerText)、計算進度條長度(容器寬度、乘以圓周率)、更新顏色 … etc。

CSS 變數讓我們設計的樣式更加 General

由於 CSS 擁有 calc的 function,讓我們可以做簡單的運算。如以下的 CSS value 如果是具有關係的,我們可以透過 CSS varcalc 函數,來進行設計,將來調整時,只需要動到基準變數。

.box{
box-shadow: 5px 10px 15px gray
}

v.s.

.box{
--base-shadow: 5px;
box-shadow: var(--base-shadow) calc(2 * var(--base-shadow)) calc(3 * var(--base-shadow));
}

CSS 變數 與計算可以取代 JavaScript 多少事呢?

  • 計算進度條長度(容器寬度、乘以圓周率)
  • 更新顏色

首先我們的 Base 就是進度的值,該 Base 影響我們顏色的呈現、進度條的長度。

進度條的長度,即是進度與圓周率之間的關係(由 0 到周長趨近)、顏色則是進度與 R、G、B 之間的關係(由紅向綠趨近)。

Photo by Alex Radelich on Unsplash

這樣我們不是還是得操作innerText,何來單向綁定呢?

  • 更新進度的文字

關於這個目標我們可以透過偽元素(Pseudoelement)的 content 來輔助完成,於是我們可能會這樣寫:

.number::before{
content: var(--number) "%" ;
}

但是顯然 content 不允許你這種寫法,即使你把 % 刪掉,content 仍然不能顯示文字。

這時我們就會需要使用到 CSS counter ,用法如下:

.number::before{
counter-reset: progress var(--progress);
content: counter(progress) "%";
}

這時就可以把我們 progress 這個基準變數一起秀在視圖層上。

讓我們來公佈文章開頭的答案

其實很明顯了,當所有動畫、顏色的關係都可以 base variable 與 calc 計算出來時,JS 基本上啥事也不用幹了。

答案就在標題裡啦, 1 行:

  • 選component一行
  • 註冊事件一行
  • 生成隨機數一行
  • 設定 CSS 變數一行 (***)←正確來說只有這行與跟整個動畫行為有關

尾聲

CSS3 變數實在是太方便了,加上 calc 、 counter 的能力,我們完全可以把畫面該做的計算全部交託給 CSS 來做!

之前曾經與朋友討論控制動畫執行順序的問題,那時我便發現,如果能夠把 delay、duration 之間的關係設計好,我們可以在 CSS 完全做掉這些事,而非交託給進度不准的 setTimeout 。

But,透過 JavaScript 去操作它時,還是相當痛苦的。

在 JavaScript 中,el.style.setProperty("--progress",20) 這個語法還是稍微讓人覺得有些麻煩。

當我們得在同個容器裡頭更新多個 CSS 變數時,就會得出現下列的寫法:

container.style.setProperty("--R", 255);
container.style.setProperty("--G", 125);
container.style.setProperty("--B", 74);
container.style.setProperty("--FZ", "20px");

但 CSS 變數如此的棒,我又不想為此放棄使用它,於是我寫了一個函式庫來解決這個問題:

Caesar: Batch read / update multiple CSS3 variables !

可以把上面寫法化為如同 Object.assign 的形式

caesar.assign(container, {
R: 255,
G: 125,
B: 74,
Fz: "20px"
});

有興趣的可以觀看這個 Demo

--

--

No responses yet