<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":"[]"}
    3482 views