CSS Houdini: Properties, Values, and the Paint API

本文首发于 掘金,未经许可严禁转载
前言 最近研究了下 Houdini,它是 CSS 领域的一个重大变革,它的终极目标是实现 CSS 属性的完全兼容,其中最受关注的特性之一就是它能正确地 polyfill CSS!这么说比较抽象,它到底是做什么的呢,如何使用呢,本文来讲解一二。
基本概念 Houdini 是一组底层 api,它公开了 CSS 引擎的部分内容,使开发人员能够通过 hook 到浏览器渲染引擎的样式和布局过程来扩展 CSS。它可以让开发者直接访问 CSS 对象模型(CSSOM) ,使开发者能够编写浏览器可以解析为 CSS 的代码,从而创建新的 CSS 特性,而无需等待这些特性在浏览器中自行实现。
此外,它还能用于创造一些自定义的,带有类型检查和默认值的 CSS 属性。
Houdini API 介绍 一、CSS property 和值 API CSS 其实已经有自定义属性了,这能解锁太多新玩法。而 CSS Properties and Values API 的出现进一步推动自定义属性,还允许自定义属性添加不同的类型,允许属性类型检查、设置默认值以及定义属性是否可以继承值,大大增加自定义属性能力。
这个 API 最大卖点是开发者可以在自定义属性上做动画,这是仅凭借现在技术是做不到的。
先来看一个案例,CSS 变量可能很多人都有使用过,它之所以神奇,是因为它是动态的。但它的弱点之一是无法转换,如果你尝试为变量设置动画,它只会从一个属性翻转到另一个属性,之间没有过渡效果。
CSS Houdini: Properties, Values, and the Paint API
文章图片

CSS Houdini: Properties, Values, and the Paint API
文章图片

Codepen Demo
这是因为 CSS 变了没有任何意义,它没有任何类型,因此浏览器不知道特定变量是否是颜色、百分比、数字等。
CSS Houdini 提供了为变量分配类型的能力。如果你为变量分配了错误类型的值,浏览器将忽略它并选择默认值。
CSS.registerProperty({ name: '--start', syntax: '', inherits: true, initialValue: 'purple' })

CSS Houdini's Properties 和 Values API 允许我们为 CSS 变量指定类型。使用强类型的 CSS 变量,它最终可以被转换。最流行的用例之一是动画渐变。
CSS Houdini: Properties, Values, and the Paint API
文章图片

CSS Houdini: Properties, Values, and the Paint API
文章图片

Codepen Demo
使用方式有2种
1、CSS 中定义 CSS 属性 @property
@property --my-color { syntax: ''; inherits: false; initial-value: #c0ffee; }// 使用: div { color: var(--my-color); }

@property --property-name 中的 --property-name 就是自定义属性的名称,定义后可在 CSS 中通过 var(--property-name) 进行引用
  • syntax:该自定义属性的语法规则,也可以理解为表示定义的自定义属性的类型
  • inherits:是否允许继承
  • initial-value:初始值
2、JS 中定义 CSS 属性 CSS.registerProperty
window.CSS.registerProperty({ name: '--my-color', syntax: '', inherits: false, initialValue: '#c0ffee', })// css中使用 div { color: var(--my-color); }

二、CSS Paint API(Worklets) 该 API 使我们能够通过 Canvas 以编程方式为需要图像的任何 CSS 属性创建图像。此类属性的示例是 background-imageborder-image
CSS Paint API 可以简单理解为把 Canvas 作为普通元素的背景图,也就是说 CSS 的 background-image 就是一个 Canvas,可以利用这个特性为很多元素绘制背景特效。
小示例
下面我们为 textarea 写一个棋盘背景,效果如下:
【CSS Houdini: Properties, Values, and the Paint API】CSS Houdini: Properties, Values, and the Paint API
文章图片

textarea { background-image: paint(checkerboard); }

// checkerboard.js class CheckerboardPainter { paint(ctx, geom, properties) { // Use `ctx` as if it was a normal canvas const colors = ['red', 'green', 'blue']; const size = 32; for(let y = 0; y < geom.height/size; y++) { for(let x = 0; x < geom.width/size; x++) { const color = colors[(x + y) % colors.length]; ctx.beginPath(); ctx.fillStyle = color; ctx.rect(x * size, y * size, size, size); ctx.fill(); } } } }// Register our class under a specific name registerPaint('checkerboard', CheckerboardPainter);

Codepen Demo
它的固定用法套路分三步走:
  1. CSS 中 paint(abc);
  2. JS 添加模块 CSS.paintWorklet.addModule('xxx.js');
  3. xxx.js 中代码套路固定,在下面注释位置写绘制代码即可;
    registerPaint('abc', class { paint(context, size, properties, args) { // 绘制代码在这里.... } });

    registerPaint 方法注册了一个 Paint 类 abc 以供调用,这个类的核心在于它的 paint 方法。paint 方法用于描述自定义的绘制逻辑,它接收四个参数:
  4. context:绘图的上下文,API 全部都是来自 Canvas 的 CanvasRenderingContext2D,不过为了安全限制,有些 Canvas 中的 API 是不能使用的。
  5. size:节点的尺寸信息,同时也是 canvas 可绘制范围(画板)的尺寸信息。
  6. properties:包含节点的 CSS 属性,需要调用静态方法 inputProperties 声明注入。
  7. args:CSS 中调用 Paint 类时传入的参数,需要调用静态方法 inputArguments 声明注入。
三、CSS Typed OM 以前我们修改 DOM 元素样式,实际上我们操作的是 CSS 的对象模型 CSSOM。而 CSSOM 简单说就是能让 JS 操作元素样式的 API:
const el = document.getElementById('el'); el.style.opacity = 0.3;

这会存在一个问题 el.style.opacity 的类型并非一个数字,而是一个字符串。如果要像将其进行数学计算,那就需要先进行类型转换,所以这就是 Typed OM 要解决的问题。
将 CSSOM 值字符串转换为有意义的类型化 JavaScript 表示并返回可能会导致显着的性能开销。该规范将 CSS 值公开为类型化的 JavaScript 对象,以使操作它们更容易且性能更高。
它的卖点有:
  1. 更好的性能,由于减少了字符串操作,对于 CSSOM 的操作性能得到了更进一步的提升,由 Tab Akins(github 用户)提供的测试表明,操作 Typed OM 比直接操作 CSSOM 字符串带来了大约 30% 的速度提升;
  2. 错误处理,对于错误的 CSS 值,将会抛出错误;
  3. 在数值对象上调用简单的算术运算方法,绝对单位之间还能方便得尽兴单位转换;
读取和赋值用法
在 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 对于其它 Houdini API 的意义:Typed OM 的使用在为往后更高效地发展各个 Houdini 标准打下了基础,包括自定义属性,布局以及绘制相关标准。
CSS Typed OM 解决了开发时修改数值的问题,同时通过减少字符串操作增加了总体的操作性能,使得我们在操作 CSSOM 不仅方便还高效,配合 requestAnimationFrame 还能制作出性能更优的自定义动画。
四、CSS Layout API CSS Layout API 的招牌就是让开发者自定义布局方式,比如瀑布流等。让 Web 布局有更多的想象空间,由于浏览器还没有完全开放给开发者,并且学习成本较高,需要CSS和JS同时具有一定造诣才能驾驭,因此本文不做展开。
Reference
  • https://drafts.css-houdini.or...
  • https://zhuanlan.zhihu.com/p/...
  • https://aysha.me/2019/08/css-...
  • https://www.zhangxinxu.com/wo...

    推荐阅读