【CSS】Houdini, CSS的成人禮

  • 2019 年 10 月 3 日
  • 筆記

 

前情提要

  • CSS:老闆,你看ES9,ES10都出來了,您看我的事情什麼時候…

  • W3C: 這不是正在走着流程嘛!小C你不要心急!

  • W3C:(語重心長)你看啊,我們先(1)提個開發提案章程, 然後再批准成立工作組(2)緊接着工作組建立標準和指南,然後再修改修改。(3)再然後啊,給顧問委員會技術報告審核一下(4)最後瀏覽器廠商再實現一下 這不就完事了嗎?大概不到10年咱們就全部搞定了吧

  • CSS: 。。。。。。 (難過)

  • W3C:(頓了頓):但是這些年啊,你的刻苦努力我們都是看在眼裡的!

  • CSS:所以!!??(興奮加期待)

  • W3C:所以啊,我們要感謝你為前端社區,所作出的貢獻!

  • CSS:。。。。。。(沮喪

  • W3C:但是呢!考慮到你設備老舊,是該更新一下啦。

  • CSS:請問是!?(興奮加期待,還有微微的遲疑)

  • W3C:沒錯,這是老闆我送給你的步入成年的禮物—— Houdini

什麼是Houdini

Houdini包含6組API

S1. Paint API

簡單地講,Paint API就是允許你通過JavaScript註冊一個背景函數,類似於linear-gradient()那種,在定義時候可以提供Canvas的2Dcontext給你自行繪製,你通過JS註冊這個背景函數後呢,就可以自由地在CSS中通過以下方式使用該背景
background: paint(背景函數名);

如果想了解Paint API的MDN入門指導請點擊這裡 ,因為這個MDN上是有入門文檔的,所以就不放草案了
 

S2.Layout API

可以允許你自行定義布局,並通過以下方式使用:
display:layout(自定義布局名)

這意味着什麼呢?我們常用的flex布局, 柵格布局都可以重新通過display進行定義和使用,非常方便。甚至,如果/假如Layout API早出來10年的話,我們甚至可以為這些類似於flex等的新興屬性定義polyfill!, 就像我們為ES6中的proxy對象,promise對象分別添加proxy-polyfill,promise-polyfill一樣,實現對低級瀏覽器的兼容,這無疑讓人感到高興。
 
Layout的定義雖然是為布局使用的,但實際上,根據W3C草案,它還有控制定位和overflow等的能力,總之,除了動畫意外的疑難雜症,Layout幾乎都能夠涉足。如果你想仔細了解:layout的W3C草案
 

S3. AnimationWorklet

這麼關鍵的場合怎麼能少得了動畫呢?它可以控制動畫的效果。該 API 使用戶可以在專有的線程中去運行動畫,從而大大降低了主線程的壓力。如果想了解AnimationWorklet的W3C草案內容請點擊這裡這個草案在今年的6月底剛剛發佈

S4. Properties & Values

用於自定義CSS屬性並為其約定類型,行為和默認值。可以看作less的@式定義和Sass的$式定義的2.0加強版。順便一提,它經常和前3個API搭配使用。

S5. Parser API

允許開發者自由擴展 CSS 詞法分析器,比如新的媒體規則、新的偽類、嵌套等等。時至今日,它還沒有形成一個完整的草案,只是初具模型,大家可以閱讀 Parser API草案(W3C)

S6.Typed OM

用人話解釋就是,原本我們不是可以通過element.style.cssText去修改CSS樣式嘛,大佬們覺得這樣的字符串操作麻煩而且速度很慢,所以定義了一組CSS專有的接口對象去進行操作,Typed OM提供了與底層值交互的接口,通過使用專門的JS對象來表示它們。除此外還增加了一些其他的API 點擊這裡查看MDN的Typed OM的入門指導

為什麼說Houdini是CSS的成人禮?

這個問題等同於:
問題:結合CSS的產生背景和歷史淵源,請問如何客觀評價Houdini的歷史地位?(本小題10分,請考生答題時不要超出裝訂線) ( 難道我們在考歷史題嗎?逃Σ( ° △ °|||)︴
 

A. 為CSS新特性提供polyfill

它的主要作用在於給予開發者更多開發CSS的自由度,推動CSS新特性發佈的進程,同時為未來的那些像flexbox這樣優秀的特性提供polyfill,讓我們可以不再需要顧忌兼容性,而能夠儘快地使用新發佈的CSS特性,當然,CSS的生態也會因此更為繁榮。
 
舉個例子,我們在使用ES6的Promise的時候,怎麼考慮讓它在低版本瀏覽器不報“Promise is undefined”的錯誤呢?很簡單,只要加個promise-polyfill就可以了,如果你想一本萬利,那麼導入個babel-polyfill,所有(嚴格的說是大部分)ES6的新對象都可以放心用了。
 
試着想一下,假如Houdini比flex早出來10年,這個時候還需要擔心flex兼容性嗎?不需要了,我們可以從NPM社區上下一個包,這個前人寫好的npm包註冊了一個layout方法,你只要下載這個包,在index引用運行一下,然後可以肆無忌怛的通過使用flex了,想怎麼浪就怎麼浪

 

B. Houdini的作用是為CSS提供進一步的完善

Houdini出來是16年的事情了,當時的話大家都覺得個這個新東西挺有發展潛力的,但是我覺得嘛,要客觀看待,Houdini做的事情,其實很多時候,原本我們就可以做了,舉個例子:
  1. Paint API: 在JS領域裏直接使用Canvas標籤和包裝起來的相關函數也是可以做的

  2. Layout API:提供的是大幅度的自定義布局的功能,但問題是。。。flexbox和grid已經把大多數場景給肝了。Layout要是早出來還好,這比flex完了這麼多年,就感覺有點尷尬

  3. Typed OM:提供了直接操作CSS屬性的對象接口,但問題是CSSOM的標準出來也不少時間了呀,相比之下Typed OM的功能好像就失了一些新意。。。(某個表情包老頭估計會說:別筆筆,一梭子ele.style.cssText不就完事了嗎!)

  4. 其他,想到再更

我們可以理解為,Houdini在為一開始CSS沒有設計想要,並設計進去的一些東西做一些補充(XX可能會遲到!但永遠不會缺席。但問題是沒打到卡是要扣工資的呀~)
 
  • CSS:大師!我感覺我在傳承了Houdini的靈力加持後功力大增!

  • 大師:那尼瑪是因為你一開始的內力。。。emmm

 

啊~下面又到了我最喜歡的互懟環節了呀

  1. 告別CSS

  2. 為什麼CSS一開始這麼難學

  3. 前端工程師討厭寫CSS是什麼心態?

為什麼說Houdini是CSS漫長的成人禮?

至今為止,剛剛在can i use上查到的結果顯示,目前houdini的七大特性,除了Paint API和TypedOM外,其餘幾個API在瀏覽器上可以說都是全軍覆沒的狀態(或者說剛剛萌芽更合適一些?),就算是Paint API也好,它也僅僅只在Edge76,Chrome66和Opera52以上實現了技術紮根, 其餘瀏覽器,哪怕是IE,Firefox,Safari的最新版本,也都尚未實現

 
下面這張是谷歌上搜到的,2018年底製作,現在可以認為絕大部分仍然適用

 

 

可能有同學會問了,上面寫的Chrome-Canary是什麼意思呢?額。。。canary直譯為“金絲雀”,屬於和dev,beta相類似的版本的概念範疇,(你就理解為內測版吧,逃~)根據相關資料提示,Layout API在Chrome-Canary上實現部分支持,但是我試用了一下發現還是用不了(心塞),控制台CSS.layoutWorklet打出來是undefined,所以評論區有高人還請指教下啦~

 

Chrome-Canary中國區下載鏈接

實戰Houdini之Paint API

那咱們就寫一個咱們可以在Chrome上跑的demo好了
 
首先,我們需要編寫一個JavaScript文件,我們命名為paint.js,然後通過registerPaint方法註冊一個paint方法,paint方法可以繪製div的背景,例如下面我們把這個paint方法命名為circle,這意味着它可以通過background: paint(circle)去使用繪製的畫布。我們上面也說過,我們註冊paint方法時系統會提供ctx作為參數,這個ctx是HTML5/Canvas的2Dcontext的子集,實現了除了文字操作外的大多數方法和屬性。(文字操作指的是ctx.fillText或者ctx.strokeText這一類方法)。
 
下面我們來通過JS註冊一個paint方法,來為div添加一個background,這個paint方法命名為circle,它的功能是為div提供一個半徑為Math.min(長,寬)的實心圓,圓的背景色可在CSS的上下文代碼塊中通過–color這個屬性名指定。

 

paint.js的全部代碼如下
// paint.js  registerPaint('circle', class {      // 決定了paint方法中props中能獲取的CSS屬性值      static get inputProperties() { return ['--color']; }        // 繪製一個半徑為長或寬的最小值的圓形作為背景      // ctx是Canvas的ctx的子集,實現了除文字操作外的大多數方法和屬性      paint(ctx, size, props) {          // size表示使用該paint方法的div的長和寬          const width = size.width / 2;          const height = size.height / 2;          const radius = Math.min(width, height);          // props.get表示將根據inputProperties返回的鍵值從CSS代碼塊中獲取相應屬性          const color = props.get('--color');          // 給畫筆着色          ctx.fillStyle = color;          // 開始動筆繪製          ctx.beginPath();          // 以width,height為圓心,radius為半徑畫圓圈,從0度畫到360度          ctx.arc(width, height, radius, 0, 2 * Math.PI);          // 用fillstyle把圓圈軌跡內部進行顏色填充          ctx.fill();          // 搞定!      }  });

(代碼中的具體API我們過會再仔細解釋,我們先把代碼跑通再說)

 

然後,僅僅這樣做是不夠的,我們還需要在主線程里,例如main.html的腳本里。通過下面這個API去加載我們剛剛定義的paint.js
CSS.paintWorklet.addModule('paint.js');

注意,paint.js內部會形成一個封閉而獨立的,叫worklet的作用域,它和全局Window是不一樣的!不要在這裡嘗試使用fetch等方法。

 

上面兩步做完了,我們就可以使用CSS去使用我們剛剛定義的paint函數了
<!-- HTML -->  <div class="foo"></div>  <!-- CSS -->  <style>  .foo {     border: 1px solid blue;     width: 200px;     height: 200px;     /*指定背景色為紅色*/     --color: red;     /*使用剛剛註冊的paint方法*/     background-image: paint(circle);  }  </style>

 

我們看下效果

 

 

github項目代碼如下

https://github.com/penghuwan/houdini-module

 

好,我們回過頭來解釋下paint方法裏面的邏輯
(1)size 它是一個對象,保存了使用這個定義paint方法的div的長和寬,可以分別通過size.width和size.height去訪問
registerPaint('circle', class {      // ctx是Canvas的ctx的子集,實現了除文字操作外的大多數方法和屬性      paint(ctx, size, props) {          // size表示使用該paint方法的div的長和寬          const width = size.width / 2;          const height = size.height / 2;          // ...      }  });

(2)props我們可以通過props參數獲取CSS上下文代碼塊(指的是paint(circle)在的那個代碼塊)的其他CSS屬性,但是這個屬性需要在inputProperties函數中進行聲明,聲明的方法是在函數中返回一個數組,數組項為屬性名稱
// paint.js  registerPaint('circle', class {      // 決定了paint方法中props中能獲取的CSS屬性值      static get inputProperties() { return ['--color']; }      // ctx是Canvas的ctx的子集,實現了除文字操作外的大多數方法和屬性      paint(ctx, size, props) {          const color = props.get('--color');      }  });  // style  .foo {     /*指定背景色為紅色*/     --color: red;     /*使用剛剛註冊的paint方法*/     background-image: paint(circle);  }

(3)ctx:這個參數上文已經多次介紹,這裡不再贅述
 

警告,下面講的都是當前沒有任何stable瀏覽器可以運行的代碼,我是根據W3C的草案和示範代碼要求來的,正所謂——姜太公寫代碼,願者Debug

 

Layout

<抱歉寫的太累了,休息下,本部分待會再更>

Animation

<抱歉寫的太累了,休息下,本部分待會再更>

Propertis和values

<抱歉寫的太累了,休息下,本部分待會再更>

Typed OM

<抱歉寫的太累了,休息下,本部分待會再更>

 

CSS!你的Houdini來啦

 

但是你用不了,哈哈哈哈!

 
本文完

 

參考資料

  1. Houdini:CSS 領域最令人振奮的革新

  2. Chrome提供的Houdini Demo

  3. 1中Demo的在線運行地址(注意很多是暫時跑不了的)

  4. layout的API草案(W3C)

  5. Painting API的入門指導

  6. Parser API草案(W3C)

  7. AnimationWorklet工作草案(W3C)

給大家一點點建議,谷歌英文資料吧。。。很多乾貨