<h1 class="workspace">
<div class="left">CSS</div>
<div class="right">JS</div>
<div class="var">Variables</div>
</h1>
<style>
.present .workspace {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-image: linear-gradient(100deg, #e7e7d0 50%, #18182f 0);
text-align: center;
font-size: 15vmin;
font-weight: 900;
line-height: 1;
color: #fff;
}
.present .workspace .left,
.present .workspace .right {
position: absolute;
top: 15%;
bottom: 0;
width: 50%;
flex-grow: 1;
height: 100%;
overflow: hidden;
mix-blend-mode: difference;
}
.present .workspace .var {
position: absolute;
top: 40%;
left: 50%;
transform: translateX(-50%);
mix-blend-mode: difference;
}
.present .workspace .left {
left: 0;
}
.present .workspace .right {
right: 0;
}
</style>
<br>
<br>
<div class="intro">
<a href="https://fb.me/rplus.tw">
<img src="https://avatars.githubusercontent.com/rplus" width="120">
</a>
<a href="https://twitter.com/RplusTW">@RplusTW</a>
</div>
Note:
Hi 大家好,
我是 R+,
今天這一場的主題是「變數握手,CSS、JS 樂無窮」
雖然 JSDC 本來跟我說,可以講一場 CSS 的主題來打 JS 的臉
我其實也是滿想的,
但是有些經驗的前端工程師應該都能理解:前端技術三支柱,並不能偏廢
純 CSS 雖然很 geek、很生猛;但還是有它的極限在,CSS 它仍然還是該以 style 為主軸
雖然有些 CSS 的 selector 可以做到一些互動的效果
但網頁的互動邏輯基本上還是以 JS 作為核心
所以今天來分享一些 CSS variables 跟 JS 互動的使用經驗
我講的也不一定都對~ 有什麼疑問的話 非常歡迎大家一起研究討論
你可以在 FB 或 Twitter 上找到我,
但技術問題能公開問就公開問
盡量不要用私訊,這樣我要是解釋地不好也沒法得到其他人的修正
若你比較害羞 也可以到 Gitter 上 cue 我,連結會附在最後面
啊 因為 Gitter 挺冷清的,不用太擔心會有很多人看到 XD
---
# <a href="https://translate.google.com/?#en/zh-TW/variables">variables</a>
`/v'ɛriəbəlz/`
<br><br><br>
Note:
JS 大家都很常用 var, let, const 這邊就不再多加介紹,
今天的主題會著重於 CSS variables,我會先介紹一下 CSS variables 的基礎知識
---
## CSS Custom Properties for Cascading Variables
<br>
* CSS Custom Properties
* CSS Variables
<br>
<br>
> spec: https://www.w3.org/TR/css-variables-1/
---
## CSS Custom Properties for Cascading Variables
> This module introduces <span><!-- .element: class="fragment highlight-green" -->cascading</span> variables as a new primitive value type that is accepted by <span><!-- .element: class="fragment highlight-green" -->all CSS properties</span>, and custom properties for defining them.
<br>
Note:
變數不稀奇,像 SASS 之類的工具也都有,但 cascading 則是 CSS variables 更加靈活的關鍵點
"all CSS properties"! 泛用
在 2015 就是候選推薦標準了 Candidate Recommendation spec,滿穩定了,大家可以安心用,不用怕 API 大改
---
## Syntax
```css
.boo {
--color-red: #c00;
color: var(--color-red, #f00);
}
```
<pre><!-- .element: class="fragment" data-fragment-index="1" --><code class="scss hljs">
.heart {
--color-red: #f00;
--color-blue: #00f;
--color-♥: var(--color-red, #900);
color: var(--color-♥);
}
</code></pre>
<br>
<br>
Note:
兩個 dash 後接 custom property
引用時使用 var 包起來,後面可以放 fallback 的屬性值
前面的 property 跟後面的 value 都非常寬容,
一般只要正常的文字都沒問題,當然特殊符號還是有可能炸掉的
但 emoji 是 ok 的~
對特殊語法有興趣的也可以去讀讀 w3c spec,這 spec 算是字數挺少的 spec 了 XD
---
#### 常見使用情境
<ul>
<li>顏色</li>
<li>尺寸</li>
<li>文字</li>
<li><!-- .element: class="fragment" data-fragment-index="1" -->media query</li>
</ul>
Note:
對,基本上就是你會去問 UI 設計師的東西
大多像是定義用的,可以想像成 spec 給的值
這些定義類的用法跟 SASS 裡的變數比較類似
就是定義後讓其它元件可以直接跟變數連動
省得異動時每個地方都要去改
這些不少文章都會優先以這類顏色、大小的明顯變化來示範 CSS variables 的威力
但如果跟 media query 混在一起後
就會有比較有趣的變化
來看一下
---
```css
:root {
--base-font-size: 14px;
}
.h2 {
font-size: var(--base-font-size);
}
@media (min-width: 600px) {
:root {
--base-font-size: 16px;
}
}
```
note:
像這一例中,我們分別在全域與 media query 裡使用 CSS variables
可以讓不同視窗寬度下有不同的基礎字級
你可能會說,這個就直接在 media query 裡改 `.h2` 的字級就可以了呀
OK,那兩個呢?
---
```css
:root {
--base-font-size: 14px;
}
.h1 {
font-size: calc(var(--base-font-size) * 1.5);
}
.h2 {
font-size: var(--base-font-size);
}
@media (min-width: 600px) {
:root {
--base-font-size: 16px;
}
}
```
Note:
若設計師希望在不同寬度下有不同的字級,但相對大小仍維持相同的比例
可以觀察到:
在愈多元件相依於同一個值的時候,CSS variables 就愈能體現它的便利性
只是這邊也需要留意:
除非有管理上的需求,盡量在複數元件都相依的才使用,而不是全部都直接改用
而與 media query 搭配的這種例子中
也是更明確地展示了 CSS variables 中 cascading 繼承的重要性
---
## cascade with `font-size`
```scss
.ooo {
width: 5em;
height: 5em;
font-size: 20px;
&.big {
font-size: 50px;
}
.o {
width: 1em;
height: 1em;
transform: translateX(2.5em);
}
}
```
note:
這邊先將時光往前移一點,
在還沒有 CSS Variables 的時候 我們如何利用繼承特性,
我們比較常見的類似用法是使用 `font-size` 控制元件的尺寸
然後再使用 `em` 就可以便利地調整相對尺寸大小
例子中在不同 class 下了不同的 `font-size` 即可一起控制整體元件的尺寸
控制子元件尺寸也可以用 `%` 處理,
但若需要處理 `transform` 的時候,使用 `%` 就會遇到一些問題
不過這樣的繼承只限於很少的屬性,`color`、`font-size`…
所以 CSS variables 出來救大家了~
---
## [瀏覽器支援](http://caniuse.com/#feat=css-variables)
* Global: 78.11%
* Taiwan: 84.6% <sub><small>(5% IE 11)</small></sub>
---
## 瀏覽器支援
* Firefox v31 <small><sub>( 2014 / 06 )</sub></small>
* Chrome v49 <small><sub>( 2016 / 05 )</sub></small>
* Safari v9.1 <small><sub>( 2016 / 05 )</sub></small>
* iOS v9.3 <small><sub>( 2016 / 05 )</sub></small>
* Android WebView <small><sub>gg</sub></small>
* IE <small><sub>gg</sub></small>
* Edge v15 <small><sub>( 2017 / 04 )</sub></small>、v16<small><sub>( 2017 / 10 )</sub></small>
note:
Edge v15 is buggy
問一下,你負責的專案需要支援 IE 11 的可以舉個手嗎?
想在工作上使用 CSS Variables 的朋友們,趕快問問附近剛沒舉手的還缺不缺同事 XD
開玩笑~
---
### Fallback with CSS:
```css
@supports ((--a: 1)) {
:root {
--color-red: #f00;
}
}
```
note:
feature detection
IE 11 gg again, QQ
---
### Fallback with JS:
```js
if (window.CSS &&
window.CSS.supports &&
window.CSS.supports('--a', 0)) {
_html.classList.remove('no-css-variables');
}
```
Note:
類似 Modernizr 處理 class 的方式
CSS variables 基礎介紹到這
---
## 變數握手
<br>
<br>
<br>
<br>
<br>
<br>
<!-- .slide: data-background="https://i.imgur.com/dWHMtr7.png"-->
note:
進主題:握手
當然,這邊講的不是自己握手,而是要跟別人互動的握手
---
## 變數握手
<br>
<br>
<br>
<br>
<br>
<br>
<!-- .slide: data-background="https://i.imgur.com/88e1JJG.jpg"-->
note:
cc0 image source: https://pixabay.com/en/paw-hand-friendship-dog-human-548634/
---
## 1. set
```js
// inline-style
element.style.setProperty('--a', 2);
```
<br>
<br>
<br>
<br>
note:
一般比較常用的是 set 值
在 JS 判斷一些環境後,給定特別設定值
再由 CSS variables 在 CSS 裡處理好 UI 的邏輯
React v15.6 才有支援在 style 屬性裡,
React 舊版要自己手刻 & 用原生寫法手動更新
這寫法其實也挺噁心的就是
https://github.com/facebook/react/pull/9302/files#diff-61273f1ad4383d5a3a802ac95031ade4R220
Vue
https://github.com/vuejs/vue/commit/4b8eb75c7a5544a1f65ebd679fd89639208d5976
---
## 2. get
```js
// inline-style
element.style.getPropertyValue('--a');
// any case
window.getComputedStyle(element).getPropertyValue('--a');
```
<br>
<br>
<br>
<br>
note:
get 的情境會比較少一點
我自己也是滿少用的,只有一次在處理 CSS image sprite 有用到
是也滿好玩的
有些 CSS variables 是預寫好在外部 CSS 檔案裡,這種情況就要用第二種方式才能拿到值
---
* CSS variables 擅長:
(X)與 JS 溝通
(O)在 CSS 裡處理相依
<!-- .element: class="fragment" data-fragment-index="2" -->
<br>
<br>
-----
<!-- .element: class="fragment" data-fragment-index="2" -->
<br>
要不如… 直接用 JS 設定 inline style?
note:
恩… 看到這裡,應該會覺得以前直接設定 inline style 在 DOM 上更直覺一些 XD
但像前面提到的, CSS variables 有個很關鍵的點就在於 `cascade`、繼承的特性
我們可以統一在親代層設定一次,而不是每一層都設定一次
除了減少 DOM 異動次數以外,更重要的是讓相依的值可以真的是相依,
而非依 JS 邏輯處理並依序設定後,才"看似"相依
實際來看一些例子
---
[Demo 1: parallax layers @ CodePen](https://codepen.io/Rplus/pen/GOZjKM)
<a href="https://codepen.io/Rplus/pen/GOZjKM">
<video autoplay muted controls playsinline loop
src="https://giant.gfycat.com/UniformLimpIzuthrush.webm"></video>
</a>
note:
視差效果很夯,但相對不難的實作原理,相信很多人都玩過、或套用過
而這效果其實也很適合用 CSS variables 處理
今年的 COSCUP 網站首頁也有使用到視差效果 + CSS variables,有興趣可以去參考看看:)
---
```css
.l {
transform:
translate(-50%, -50%)
translate(
calc((var(--dx) - .5) * var(--df) * 200%),
calc((var(--dy) - .5) * var(--df) * 200%)
);
}
.box { --dx: 1; --dy: .7; --df: 0; } /* default */
```
```js
const setDxDy = (evt) => {
let dx = evt.layerX / boxWidth;
let dy = evt.layerY / boxHeight;
box.style.setProperty('--dx', dx);
box.style.setProperty('--dy', dy);
};
```
note:
從這例子可以看到,我們只需要設定 dx 跟 dy 就好了
其它定位通通會經由設定好的 CSS Variables 處理好相依
那開發者的工作就會變成:去找出相依的規則來
這部份若用以前的寫法
就會是去設定每個 layer 的 `transform`,變成要觸發很多次 DOM 異動
數量一多,勢必會影響網頁效能
當然,也不是說用 CSS Variables 就通通一定效能一級棒
數量多的 CSS repaint 也是很容易有效能差而出現 jank 的情況
---
## CSS condition
```scss
// [--x, --y] = [0, 0]
// [--x, --y] = [1, 1]
transform:
translate(
calc(0% - 100% * var(--x)),
calc(0% - 100% * var(--y))
);
```
<a href="https://codepen.io/Rplus/pen/BmKZyL">
變形:<br>
<img src="https://i.imgur.com/xK3QsjC.png" width=250>
</a>
note:
有 CSS variables 後,CSS 也能做到簡單的判斷
那通常會稱作 CSS condition
透過 step function 的概念來切換不同的數值
---
## CSS Variables 真的是 Variables 嗎?
```js
// JS
i = i + 1;
```
```css
/* expected syntax for real CSS variables */
.l + .l {
--df: calc(.2 + var(--df));
}
```
<br>
<br>
note:
順便用這例子提一下為什麼正式名稱不是 CSS Variables
並沒有像 JS 變數的特性 XDD
不能用自己再賦值給自己
如果真的需要類似的功能,需要另外一組來更新數值
它真的比較適合稱作 Custom Properties
---
[Demo 2: re-order @ CodePen](https://codepen.io/Rplus/pen/VraKrr)
<a href="https://codepen.io/Rplus/pen/VraKrr">
<video autoplay muted controls playsinline loop
src="https://giant.gfycat.com/ParallelFragrantIceblueredtopzebra.webm"></video>
</a>
note:
公司實際使用:大多仍然是色票、系統 UI 尺寸
特殊情境:九宮格調整順序
比較常見的解法應該是搭配有 virtual DOM framework,直接操作 data array 的順序
那 framework 會自己去異動 DOM 的順序
但其實也可以使用 CSS variables 來處理
在移動過程中,我們其實不太需要操作真正的資料的順序
---
### CSS `order` solution
![](https://i.imgur.com/dtEYdIX.png)
source: https://www.w3.org/TR/css-flexbox-1/#propdef-order
note:
先講一下這個處理方案的主要依據是 CSS 的 `order` 屬性,
`order` 目前可以使用在 flexbox & grid system 裡,專門負責 item 的排版順序 painting order
只能吃整數值,初始值為 `0`,可以餵進 keyframes 裡 <3
---
![](https://i.imgur.com/4p8vdbu.png) ![](https://i.imgur.com/o3oiInD.png)
note:
想法是讓 初始順序 變成 目標順序,
並再加**修正量**以便排在**目標位置**
這邊的修正量是直接取半格,也就是 `5`
實際使用上跟取 `1` 沒什麼不同,只是在四則運算的算式有些些不同而已
而正確的目標位置還另外需要移動方向來判斷
往前移動的話,order 應該要比該位置的更小、往後的話應該要更大
那這移動原理基本上就是這樣的設計
---
```js
// init
tiles.forEach((tile, index) => {
well.style.setProperty(`--order-${index}`, index * BASE_NUMBER);
tile.style.order = `var(--order-${index})`;
});
// in key events
targetIndex = (targetIndex + operator + 9) % 9;
let dir = originIndex > targetIndex ? -1 : 1;
well.style.setProperty(
`--order-${originIndex}`,
(targetIndex + .5 * dir) * BASE_NUMBER
);
```
note:
在第一步驟先初始化所有的 order,並讓各方塊都吃到這個 order CSS variables
在收到 key event 時,計算一下該 **移動到哪一格** 去、還有 **移動方向**
那最終要輸出的就是:
目標 order 再加上方向的修正量 (這邊就看前面修正量是怎麼定的
---
<h3 class="ref">Reference:</h3>
* [CSS Custom Properties for Cascading Variables Module Level 1](https://www.w3.org/TR/css-variables-1/)
* [CSS Variables: Why Should You Care?](https://developers.google.com/web/updates/2016/02/css-variables-why-should-you-care)
* [CSS Custom Properties (CSS Variables) Sample](https://googlechrome.github.io/samples/css-custom-properties/index.html)
* [Using CSS custom properties (variables) - CSS | MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables)
* [It’s Time To Start Using CSS Custom Properties](https://www.smashingmagazine.com/2017/04/start-using-css-custom-properties/)
* [CSS Variables — No, really!](https://medium.com/dev-channel/css-variables-no-really-76f8c91bd34e) 👍
-----
* [Google: CSS variables](https://www.google.com.tw/search?q=css+variables)
* [Twitter: CSS variables](https://twitter.com/search?q=css+variables)
note:
「CSS Variables — No, really」這篇挺讚的,CSS variables 的常見誤解跟基本用法都講得挺清楚的~ <3
---
### 工商
<ul class="hihihi">
<li><a href="https://fb.me/rplus.tw">https://fb.me/rplus.tw</a></li>
<li><a href="https://twitter.com/RplusTW">https://twitter.com/RplusTW</a></li>
<li><a href="https://codepen.io/Rplus/">https://codepen.io/Rplus/</a></li>
<li><a href="https://www.facebook.com/HappyCSSer/">https://www.facebook.com/HappyCSSer/</a></li>
</ul>
<style>
.reveal .hihihi {
display: flex;
margin: 0;
padding: 0;
list-style: none;
}
.reveal .hihihi li {
width: 25%;
margin: 0;
padding: 0;
}
.reveal .hihihi li a {
display: block;
height: 20vh;
color: transparent;
background-size: cover;
background-repeat: no-repeat;
}
.reveal .hihihi li:nth-of-type(1) a {
background-image: url('https://lh3.googleusercontent.com/VZFTn0SFPQ-aVSbryzbEljt2ElbQJarDQ7PIY_0hLw2Ff2poTDb6LIxIsi6wU96HLKu6pisX1pXymRFL9DcrXIxna49RLA37YqBxigYuffVn4fyF_SMqcktYAg3A-Weh9oqQ1sez_w=w933-h679-no');
background-position: 0% 0%;
background-size: auto 120%;
}
.reveal .hihihi li:nth-of-type(2) a {
background-image: url('https://lh3.googleusercontent.com/x1FeYnrvZ_6MhsNNDU6fF2DQHcZULLP1DsY7J9aiK-J7P2_F48ylwaWw0kn_wWx560IAnN_-L6g2m6tzXVpHgzb40hoKFdSm42pVA6yU9Oyml4mYPcOURLvNmXSt3Qm5tVhKfdvlKA=w697-h403-no');
}
.reveal .hihihi li:nth-of-type(3) a {
background-image: url('https://lh3.googleusercontent.com/fzVpqBEsiPSRCXRgCNo1NnEm76dOhaxX36ojCKut2UV9wXecU-RbO9IjwmM1-sBqs_WjWmss-yuLSXIFWR2YvBFS5wT7tzwnZJsWGL9_I7kvyZpLheouAkYvgEsynP9Di9j8ppJ-lQ=w652-h421-no');
background-size: contain;
}
.reveal .hihihi li:nth-of-type(4) a {
background-image: url('https://lh3.googleusercontent.com/Djl4n7BAATDe1m_4AsUDW3LWHYA9mFfTiyWQg5pFpWNdZkxUje1QsGvCzjQBbWLm485Nli0_NaPEjJyY78Aqec63VyGKiBApvMkMJxkoX5TrZn73t2OVza9QCj5YZyh0O8owwqs6Dg=w886-h654-no');
}
</style>
-----
* https://www.facebook.com/groups/f2e.tw/
* https://gitter.im/f2etw/developers
* https://www.facebook.com/groups/F2ETW.share.HQ/
<style>
.container { overflow: hidden; }
.present .intro.intro { text-align: right; position: fixed; bottom: 0; right: -2% }
.intro.intro a { color: inherit; font-size: smaller; }
.intro.intro img { border: unset; vertical-align: middle; margin: 0 0 0 auto; display: block; }
.reveal mark { background-color: rgba(255,255,150, .3); }
.reveal blockquote { width: 90%; }
.reveal pre code { max-height: 90vh; }
.reveal .ref + ul li, .reveal .ref + ul a { font-size: smaller; }
</style>
---
感謝 ♥
----
### 試試身手?
https://codepen.io/Rplus/pen/LONgoq
{"metaMigratedAt":"2023-06-14T14:46:24.823Z","metaMigratedFrom":"YAML","title":"變數握手,CSS、JS 樂無窮 by Rplus -- JSDC 2017","breaks":true,"description":"Use CSS and JS Variables together to make web more wonderful","contributors":"[]"}