長期以來,咱們要修改 DOM 元素的樣式,咱們實際上操做的是 CSS 的對象模型 CSSOM。而 Houdini 中推動的又一組 CSS 對象模型 Typed OM,該標準又給咱們帶來了什麼好處呢?javascript
簡單的說來,CSSOM 是一組能讓 JS 操做元素 CSS 的 API。在瀏覽器進行頁面渲染的過程當中扮演着很是重要的角色,瀏覽器的渲染步驟大體包括:css
在平時開發中,咱們經過元素上的 style
對象去獲取元素的樣式:html
const cover = document.getElementById('cover');
cover.style.opacity; // 假設是 0.5
複製代碼
而後咱們基於這個減小一點透明度:java
cover.style.opacity += 0.3;
複製代碼
那麼,這樣作有沒有問題呢?首先咱們來看看值的類型:git
typeof cover.style.opacity; // string
複製代碼
What a suprise!,因此實際上,上面減小透明度的操做實際上產生了 0.50.3
的值,很顯然這是個有問題的操做。對於 height
等屬性,一樣的,返回了相似 200px
的字符串,getComputedStyles
返回的數值也不例外。若是你想要將這些獲取出來的數值套入一些列的數學計算中,你必須先將其轉換成數字對象。爲了解決這些問題,Typed OM 出現了。github
Typed OM 的出現,給咱們讀取以及設定數值添加了一種新的方法,不一樣於 CSSOM 中原有的字符串值的表現形式,Typed OM 將 CSSOM 的數值以 map
的形式展示在元素的 attributeStyleMap
中,規則所對應的值則是更有使用價值的 JavaScript 對象。web
attributeStyleMap
以及 computedStyleMap
對象是個 map
,這樣意味着咱們可使用標準 map
中提供的全部方法。目前各大瀏覽器廠商的實現狀況:瀏覽器
Intent to implement: 有意向實現佈局
Shipped: 已發佈性能
No signal: 暫無心圖
其中 Google Chrome 和 Opera 瀏覽器分別在 66 和 53 版中實現了。
能夠經過如下方法檢測是否可用:
window.CSS && CSS.number
複製代碼
在 Typed OM 中,數值和數值的單位是分開的,所獲取的是一個 CSSUnitValue
對象,內置數值 value
和單位 unit
兩個鍵。
// 要對一個元素的樣式賦值,除了可使用 CSS.px 構建以外,還能接受字符串
el.attributeStyleMap.set('height', CSS.px(10));
el.attributeStyleMap.set('height', '10px');
// 對於獲取,返回 CSSUnitValue 對象,訪問其 value 屬性便可獲得數字類型的值
el.attributeStyleMap.get('height').value; // 10
el.attributeStyleMap.get('height').unit; // 'px'
複製代碼
在 Typed OM 中,咱們有兩種基本的數值類型,一種是上面例子中提到的數字加單位的簡單數值,他們屬於 CSSUnitValue
類型。對於不止於單個數字加單位或使用calc
計算的表達式,均屬於 CSSMathValue
類型。
如上所述,CSSUnitValue
表達了簡單的數字加單位的 CSS 數值,同時你也能夠經過對其使用 new
來構造一個,大多數狀況下,你還能從 CSS 對象下的同名方法直接構造:
const num = CSS.number('10');
// num.value -> 10 num.unit -> 'number'
const px = CSS.px(42);
// px.value -> 42 px.unit -> 'px'
// 一樣可使用 new 方法構造一個
const deg = new CSSUnitValue(45, 'deg');
// deg.value -> 45 deg.unit -> 'deg'
複製代碼
完整的方法列表,能夠查看 CSS Typed OM 草案的內容。
若是你要表達涉及不止一個數值以及使用 calc
計算表達式的數值,則須要使用 CSSMathValue
。須要注意的是, calc
在實際使用中被瀏覽器求值以後,獲取到的是運算結果,也就是一個 CSSUnitValue 值。
既然涉及到表達式,天然少不了操做符,CSSMathValue
中還提供了基本的數學操做符:
// 求和操做: calc(100vw + -10px)
new CSSMathSum(CSS.vw(100), CSS.px(-10));
// 求積: calc(45deg * 3.1415926)
new CSSMathProduct(CSS.deg(45), CSS.number(Math.PI));
// 取相反數: calc(-10px)
new CSSMathNegate(CSS.px(10));
// 取倒數: calc(1 / 10px);
new CSSMathInvert(CSS.px(10));
// 範圍限制: calc(1px);
// 其中第一個參數爲最小值,第三個參數爲最大值,中間數值爲須要鉗制的數值
new CSSMathClamp(1, -1, 3);
// 最大值: max(10%, 10px)
new CSSMathMax(CSS.percent(10), CSS.px(10));
// 最小值: min(10%, 10px)
new CSSMathMin(CSS.percent(10), CSS.px(10));
複製代碼
以上的數學操做表達式符號均支持嵌套使用,例如須要構建表達式:calc(1px * (3px + 2em))
,能夠作以下嵌套實現:
new CSSMathProduct(CSS.px(1), new CSSMathSum(CSS.px(3), CSS.em(2)));
複製代碼
CSSMathValue
和 CSSUnitValue
他們均繼承自 CSSNumericValue
,天然地也繼承了CSSNumericValue
上的數學操做方法,方便使用:
// 加: 1px + 1px
CSS.px(1).add(1);
// 減: 1px - 1px
CSS.px(1).sub(1);
// 乘: 1px * 3px
CSS.px(1).mul(3);
// 除: 1px 除 3px
CSS.px(1).div(3);
// 比較最大值: max(50%, 50vw);
CSS.percent(50).max(CSS.vw(50));
// 比較最小值: min(50vh, 50vw);
CSS.vh(50).min(CSS.vw(50));
// 相等比較方法,返回一個布爾值 true
CSS.px(200).equals(CSS.px(200));
複製代碼
同時,加減乘除這些操做一樣支持多個參數使用
// 累加 calc(10px + 10vw + 10%)
CSS.px(10).add(CSS.vw(10), CSS.percent(10));
複製代碼
除此以外,絕對單位之間還能相互轉換:
CSS.in(9).to('cm').value;
// 22.860000000000003
複製代碼
對於 CSS Transform 的 transform
屬性,上面的基本數值表達徹底沒法知足,從而須要藉助 CSSTransformValue
,構建 CSSTransformValue
能夠傳入如下幾種參數:
CSSRotate
: 旋轉CSSScale
: 縮放CSSSkew
:傾斜CSSSkewX
:X 軸傾斜CSSSkewY
: Y 軸傾斜CSSTranslate
: 轉換CSSPerspective
: 視角與日常的 CSS 用法同樣,skew(x, y) 與分別 skewX(x) skewY(y) 產生的結果也是不同的,這點須要注意一下
用起來也是一樣的簡單:
// 轉變 transform: rotateX(45deg) scale(0.5) translate3d(10px, 10px, 10px);
new CSSTransformValue([
new CSSRotate(CSS.deg(45)),
new CSSScale(CSS.number(0.5), CSS.number(0.5)),
new CSSTranslate(CSS.px(10), CSS.px(10), CSS.px(10))
]);
複製代碼
對於CSSTranslate
類型,你還能夠訪問對象上的 is2D
方法查看當前 translate
是 2D 的仍是 3D 的。同時,還能調用 toMatrix
方法得到 DOMMatrix
矩陣對象。
對於須要描述 x/y 位置的屬性,例如 object-position
,則須要用到 CSSPositionValue
類型。
const pos = new CSSPositionValue(CSS.px(5), CSS.px(10));
// pos.y.value -> 10 pos.x.value -> 10
複製代碼
既然咱們能夠在 Type OM 的對象上使用 toString()
方法獲得字符串規則,那麼咱們是否能經過 API 將字符串規則解析成 Type OM 的類型呢?答案是能夠的。使用 CSSStyleValue
中的 parse
方法便可:
CSSStyleValue.parse('transform', 'translate(10px) scale(0.5)');
// 將會解析成 CSSTransformValue 對象
CSSStyleValue.parse('height', '2px');
// 將會解析成 CSSUnitValue 類型
複製代碼
與傳統調用 window.getComputedStyle
方法相同,元素上的 computedStyleMap
方法一樣會返回全部的計算後屬性值。但它們仍然有一些小區別。window.getComputedStyle
仍然會返回字符串數值;而對於 computedStyleMap
方法來講,返回的數值則是轉換成 Type OM 數值類型的。
document.body.attributeStyleMap.set('opacity', 1);
document.body.computedStyleMap().get('opacity').value;
// 1
window.getComputedStyle(document.body).opacity;
// '1'
複製代碼
既然 Typed OM 涉及到了 CSSOM 的數值,那麼與之相關的標準中的數值都將與此相關。前段時間看了安佳老師的文章《CSS Paint API》的同窗可能會對裏面的棋盤例子有印象,CSS Paint API 對 paint 參數的輸入值其實也是 CSS Typed OM 中的數值類型。
除此以外,Typed OM 的使用在爲日後更高效地發展各個 Houdini 標準打下了基礎(包括自定義屬性,佈局以及繪製相關標準)。
CSS Typed OM 解決了開發時修改數值的問題,同時經過減小字符串操做增長了整體的操做性能,使得咱們在操做 CSSOM 不只方便還高效,配合 requestAnimationFrame
還能製做出性能更優的自定義動畫。
drafts.css-houdini.org/css-typed-o…
developers.google.com/web/updates…
rocks1635.rssing.com/chan-409413…
感謝安佳老師對本文提出的修改建議