likes
comments
collection
share

谈谈CSS Houdini

作者站长头像
站长
· 阅读数 19

本文来自 飞猪前端团队成员 - 影羽 的内部分享

谈谈CSS Houdini

demo by @andreban

什么是CSS Houdini

Houdini 是一组底层 API,它们公开了 CSS 引擎的各个部分,从而使开发人员能够通过加入浏览器渲染引擎的样式和布局过程来扩展 CSS。 Houdini 是一组 API,它们使开发人员可以直接访问CSS 对象模型 (CSSOM),使开发人员可以编写浏览器可以解析为 CSS 的代码,从而创建新的 CSS 功能,而无需等待它们在浏览器中本地实现。 - MDN

设计初衷

不同于JS新特性可以通过类似babel等transform或者polyfill的方式来较快的使用上。CSS的新特性会经历长期的规范推进和浏览器厂商实现最后到浏览器版本覆盖才能够很好的使用。CSS的polyfill受制于浏览器限制,能够影响到渲染部分的阶段及其有限(DOM操作和部分CSSOM操作),而且很容易触发浏览器渲染步骤导致性能问题。所以它设计之初是为了解决这种CSS的新特性需要长周期才能适用的情况。

谈谈CSS Houdini

Houdini API概览和应用

W3C规范中将Houdini API包含了Painting API、Parser API、Properties and Values API、Typed OM、Font Metrics API 、Layout API、Animation Worklet、Box Tree API。

其中这些API穿插在整个浏览器渲染过程的方方面面,以此来达到高性能和定制性的目的。

谈谈CSS Houdini

上面这个图很好的诠释了Houdini 的API们是如何运作的,首先在计算阶段,CSS Typed OM 扩展了CSSOM的能力,使之不仅仅局限于字符串的操作,从而可以更好的通过JavaScript来以对象形式进行访问和操作。Properties and Values API则可以更加自由的去注册具有自定义继承,初始值和类型的CSS变量或者方法。其次在渲染阶段,通过在渲染过程中执行的独立于JavaScript主线程的Worklets脚本,分别通过Layout API,Painting API等去控制自定义的CSS在浏览器中的渲染。

Houdini API

由于Houdini API依旧在不断发展的阶段,大部分功能还在缓慢的在浏览器和规范化进程中推进,这里主要介绍几个推进的比较好的,更加具象的API。

CSS Typed OM 1

首先CSS Typed OM API提供了更好的CSSOM操作能力,简单来说就是把JavaScript的Object这种描述方式应用到了CSSOM上去,可以通过对CSS属性的对象化操作来替代之前的字符串操作。官方提供了一个例子:

// 修改元素高度 - now
// 需要提取字符串数字部分,对数字部分进行运算,将运算好的拼接成字符串重新赋值
let heightValue = target.style.height.slice(0,-2);
heightValue++;
target.style.height = heightValue + 'px';

// 修改元素高度 - Typed OM
// 获取高度,对高度进行运算,赋值
let heightValue = element.attributeStyleMap.get('height');
heightValue.value++;
target.attributeStyleMap.set('height', heightValue);

这样就使得CSSOM的操作更加的“程序化”和自然。

另一方面,Typed OM也描述了CSSOM里面很多的变量及单位的定义,对后面的进一步Houdini使用也打好了基础。

谈谈CSS Houdini

CSS Properties and Values API 1

Properties and Values API 提供了可以通过JavaScript或者@property自由组合重新设计一个自定义的CSS 属性的能力。在以前的CSS使用当中我们没办法控制一个CSS属性的一些固有属性,比如是否能够继承,默认初始值是多少等等。通过前面Typed OM定义出来的一些属性和值,我们就可以根据自身诉求来创造出最佳的CSS属性和相应值。比如官方示例中:

<script>
// 注册自定义属性--stop-color,不可以被继承,类型时color,初始值是rgba(0,0,0,0)
CSS.registerProperty({
  name: "--stop-color",
  syntax: "<color>",
  inherits: false,
  initialValue: "rgba(0,0,0,0)"
});
</script>

<style>
.button {
  --stop-color: red;
  background: linear-gradient(var(--stop-color), black);
  transition: --stop-color 1s;
}

.button:hover {
  --stop-color: green;
}
</style>

简单总结下它带来的一些好处有:

  • 可以制定具体变量类型

  • 可以支持渐变动画

  • 可以设置初始值以及继承,控制后代元素的样式表现

CSS Painting API 1

Paint API从使用上来说就像一个使用方便的绘制自由的Canvas2D画布。它提供了对元素的background, border, or content的绘制填充能力,可以将想要的内容画上去,并且支持参数定制和变化。官方对此API设定的原因有如下几点解释:

  • 简化DOM渲染 - 当前很多“特殊”的CSS实现是通过堆叠DOM等操作去实现绘制和渲染,那么直接使用canvas画布渲染可以减少CPU内存等占用,提升页面性能。
  • 渲染流程效率 - 前面提到了 Worklets 的方式,可以介入到浏览器渲染流程当中,是其他方式效率比拟不了的。
  • 页面绘制效率 - 这个比较清晰,这种会绘制方式可以保持浏览器局部渲染(局部repaint),减少页面整体渲染的可能。
  • 扩展性 - 这个是Houdini设计初衷,可以不依赖浏览器的实现去自己扩展需要的绘制能力,比如要实现虚线边框则不用等浏览器自身去实现(ps.扩展这个本身需要浏览器支持)

Paint API的使用也不复杂,熟悉canvas的同学应该更加容易上手。主要由2部分构成:

<!DOCTYPE html>
<style>
  #example {
    --circle-color: deepskyblue;

    background-image: paint(circle);
    font-family: sans-serif;
    font-size: 36px;
    transition: --circle-color 1s;
  }

  #example:focus {
    --circle-color: purple;
  }
</style>

<textarea id="example">
  CSS is awesome.
</textarea>

<script>
    CSS.registerProperty({
      name: '--circle-color',
      syntax: '<color>',
      initialValue: 'black',
      inherits: false
    });
    CSS.paintWorklet.addModule('circle.js');
</script>

registerPaint('circle', class {
  static get inputProperties() { return ['--circle-color']; }
  paint(ctx, geom, properties) {
    // Change the fill color.
    const color = properties.get('--circle-color');
    ctx.fillStyle = color.cssText;

    // Determine the center point and radius.
    const x = geom.width / 2;
    const y = geom.height / 2;
    const radius = Math.min(x, y);

    // Draw the circle \o/
    ctx.beginPath();
    ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
    ctx.fill();
  }
});

即可在background中绘制出想要绘制的图案。

谈谈CSS Houdini

同时houdini.how/上有大量的炫酷的效果可以去观赏。

CSS Layout API 1 & CSS Animation Worklet 1

最后介绍的是Layout 和 Animation API,这两个API由于推进进展比较缓慢,所以还属于非常不成熟的状态,各大浏览器的实现及其有限,只做下简单介绍。详细可以浏览具体规范。

Layout API放开了对布局的一些限制,可以将自己的布局算法/函数应用到实际场景中,构建出类似于Flexbox等布局方式,比如瀑布流布局之类。

Animation Worklet API则是将动画能力开放出来,能够通过Worklet中的脚本来控制动画的函数,运行时间,结束状态等等,预想中能够自然实现诸如视差滚动,自定义缓动动画等等。

现状和活跃度

首先是浏览器支持现状,如图(截止到21年5月,后面没有更新了🐶)。可见对于基础的Typed OM,priperties以及Paint API各方面支持的还挺不错,Safari也都在逐步支持中。而Layout 和 Animation等则依旧比较遥远。

谈谈CSS Houdini

活跃度方面W3C规范草案更新还是比较频繁,最近的更新时间是22年7月还是在活跃中。

谈谈CSS Houdini

个人理解

CSS Houdini 设计初衷十分有远见和强大,但是也有比较明显的局限性。一方面是老生常谈的规范推进和浏览器厂商的实现。另一方面如MDN上说的:

能力越大,责任越大!在 Houdini 的帮助下你能够在 css 中实现你自己的布局、栅格、或者区域特性,但是这么做并不是最佳实践。CSS 工作组已经做了许多努力来确保 CSS 中的每一项特性都能正常运行,覆盖各种边界情况,同时考虑到了安全、隐私,以及可用性方面的表现。如果你要深入使用 Houdini,确保你也把以上这些事项考虑在内!并且先从小处开始,再把你的自定义 Houdini 推向一个富有雄心的项目。

CSS Houdini 有很高的理解成本,需要有足够的能力和想象力,而简单的实现又有很大的可替代性(可以简单通过其他方式实现)。

但是从另一个方面,CSS Houdini 将浏览器的渲染流程进一步的“坦诚相待”,一定程度的白盒化了渲染过程,好像试图要教会我们怎么做渲染,那对前端技术而言则不可谓是个深入学习渲染原理的方式。

参考