單行 CSS3 variable 操作 實現邏輯至視圖層單向綁定
當我們將複雜的動畫、過場行為完全交託給 CSS 之後,JavaScript 只需在頂層設定其使用的策略與變數,CSS 即可自行產生對應規格的動畫,而非透過 JavaScript 來模擬動畫、HTML 改變的硬核,避免效能的 Impact 。
寫在文前
讓我們在文章開始之前,一起來想像如左側的效果,完全不透過模板語言去綁定資料。
需要撰寫多少行的 JavaScript 程式碼呢?
非關:註冊事件、產生隨機數
有關:更新進度的文字(操作DOM innerText)、計算進度條長度(容器寬度、乘以圓周率)、更新顏色 … etc。
CSS 變數讓我們設計的樣式更加 General
由於 CSS 擁有 calc
的 function,讓我們可以做簡單的運算。如以下的 CSS value 如果是具有關係的,我們可以透過 CSS var
與 calc
函數,來進行設計,將來調整時,只需要動到基準變數。
.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 之間的關係(由紅向綠趨近)。
這樣我們不是還是得操作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