长期以来,咱们要修改 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…
感谢安佳老师对本文提出的修改建议