开门见山,直接上货
“Houdini”一词引用自“Harry Houdini”,他是一位20世纪的著名魔术师,亦被称为史上最伟大的魔术师、逃脱术师及特级表演者。
我们都知道,浏览器在渲染网页显示样式的时候,浏览器的渲染引擎会对 CSSOM 进行解析——包括布局、绘制和合成过程。而本文要讲的内容
Houdini
是一组公开 CSS 引擎部分的底层API
,它可以让开发者能够人工干预浏览器的渲染进程,Houdini
的核心APIPainting API
就是将我们自定义的js代码插入到浏览器的绘制环节。
Houdini包括以下 API >>>
CSS Properties and Values API:顾名思义,就是与css属性相关的一系列 API,用于注册新的 CSS 属性,包括CSS.registerProperty
API 和@property
关键词(它们俩是等价的),例如我们常用 @property 常用于定义新的css属性,以实现一些css属性的动画过程,例如背景渐变色的变化过程。
CSS Typed OM:我们经常使用dom对象的style属性获取/设置它的一些 css 属性值,操作的值都是字符串。而 CSS Typed OM API
为我们提供了一些列方法,可以获取/设置具体的css属性值、单位,(你可能觉得它很鸡肋,但是它也有它的优势,例如不用考虑属性的写法、强大的数学操作和单位转换、错误捕获处理、性能更好。
CSS Painting API:它可以说是CSS Houdini的核心 API之一
,它其实就是css界的canvas,实现给元素设置复杂的自定义背景。使用时像 CSS.registerProperty 注册一个画像,然后就可以把这个画像应用于任何可以设置image的css属性上,例如background-image
、border-image
、list-style-image
等。
Worklets:工作集也是CSS Houdini的核心 API之一
。它用于在独立于主要 JavaScript 执行环境的渲染管道的各个阶段运行脚本的 API。它相当于 Web Workers
渲染管道的轻量级版本。它只有一个实例方法Worklet.addModule()
,用于将给定 URL 处的脚本模块添加到当前工作集。注册工作集后,你可以像任何其他值一样在 CSS 中使用它。
CSS Layout API、CSS Parser API、Font Metrics API:最后这三个 API目前浏览器支持性都几乎为零,所以本文就不做介绍了。
CSS Houdini使用方法很简单,我总结为有以下几个步骤,我以实现一个波浪线来进行演示(最终效果如下图):
首先我们需要创建一个js文件,我这里取名为curved-line.js
,我们要在里面实现自定义的背景效果。
在我们创建的curved-line.js
文件中添加以下代码注册工作集:
registerPaint有两个参数:registerPaint(name, classRef)
,如下代码:
//实现工作集的类的引用。
class CurvedLine {
//实现自定义css效果的代码
}
//注册Worklets工作集 "curved-line": 要注册的工作集类的名称
registerPaint("curved-line", CurvedLine);
绘图工作集类我们需要关注以下 4 个函数:
contextOptions
:该函数定义了一个上下文选项contextOptions()
,如下面的例子中返回一个简单的对象,声明允许 alpha 透明度;inputProperties
,要使用哪些CSS自定义属性(只能使用var变量,不可以使用css内置属性);inputArguments
,CSS中使用paint函数除了模块名外的其他参数,指定其类型;paint
:最关键的方法,定义绘制行为。ctx
的使用和canvas一致,size
表示绘制的大小,包括width、height等信息,properties
就是inputProperties
静态方法里定义的属性,args
就是paint
的入参,跟inputArguments
定义的对应。下面是这 4 个方法的使用示例:
//file.js 工作集类方法示例
registerPaint('paint-color-example', class {
static get inputProperties() {
return ['--my-color'];
}
static get inputArguments() {
return [''];
}
static get contextOptions() {
return {alpha: true};
}
paint(ctx, size, properties, args) {
ctx.fillStyle = properties.get('--my-color');
ctx.beginPath();
...
});
以下是绘制曲线的代码(不多说了,就是用canvas绘制曲线):
// curved-line.js
if (typeof registerPaint !== "undefined") {
class CurvedLine {
static get inputProperties() {
return [
"--curved-lineColor",
"--curved-lineSpread",
"--curved-lineWidth",
"--curved-lineHeight",
];
}
paint(ctx, size, properties) {
const lineWidth = parseInt(properties.get('--curved-lineWidth')) || 3;
const lineHeight = parseInt(properties.get('--curved-lineHeight')) || size.height;
const color = String(properties.get('--curved-lineColor')) || 'black';
const spread = parseInt(properties.get('--curved-lineSpread')) || 50;
const offset = (lineHeight < size.height) ? (size.height - lineHeight) / 2 : 0;
const midPoint = lineHeight / 2;
ctx.lineWidth = lineWidth;
ctx.strokeStyle = color;
ctx.beginPath();
ctx.moveTo(0, midPoint + offset);
let curStep = spread;
while (curStep < size.width + spread) {
const cp1x = curStep;
const cp1y = (lineHeight * 1.5) + offset;
const cp2x = curStep + spread;
const cp2y = 0 - midPoint + offset;
const x = curStep + spread * 2;
const y = midPoint + offset;
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
curStep = curStep + (spread * 3);
}
ctx.stroke();
}
}
registerPaint("curved-line", CurvedLine);
}
你要知道想要实现 CSS Houdini,即从原生层面扩展 CSS,就必须要以Worklets
工作集的方式将自定义css的代码注册到渲染引擎的进程中。上面我们也提到了Worklets
的使用方法:使用 Worklet.addModule()
实例方法将指定 URL 处的脚本模块添加到当前工作集,如下:
需要注意的是:
Worklets
可以使用的类有很多种,例如网络音频工作集AudioWorklet
,高性能程序动画工作集AnimationWorklet
,绘图工作集PaintWorklet
等,我们这里只使用了 PaintWorklet;- 工作集必须是个单独的js文件,并通过 Worklet.addModule() 引入;
- Worklet 允许ESM静态导入,即使用import导入,但是不支持
import()
动态导入,会抛出异常!
在index.html或者main.js中添加我们的工作集:
<script>
CSS.paintWorklet.addModule("curved-line.js");
</script>
paint()
函数设置炫酷背景.line{
width: 400px;
height: 200px;
--curved-lineHeight:20;
--curved-lineWidth:4;
--curved-lineColor: green;
--curved-lineSpread:15;
background: paint(curved-line);
}
<div class="line"></div>
大功告成~~~
以往我们实现复杂的效果往往都需要使用js和css进行配合,而使用 Houdini 后,相比 JavaScript ,它能够更快的解析。因为 Houdini 的代码不会像js一样等待 cssom 布局绘制完成后然后又可能造成回流重绘,它是被注入到浏览器渲染引擎的渲染进程中的。
在 JavaScript 中键入 CSS 值以及填充或发明新的 CSS 而不会影响性能终于成为可能。Houdini具有增强网络创造力的潜力。
目前 Houdini 的核心 API Painting API
兼容性还不是很好,使用时注意判断浏览器是否支持。
以下网站有一些很有意思的 Houdini Demo,感兴趣的可以看下:
https://houdini.how/
思考:因为利用 paint 绘制图案是静态单次绘制,没有办法只通过工作集来实现连续的动画过程,但是可以配合animation进行多次渲染,尤其是 steps 逐帧动画,配合 Houdini ,可以创造很多有意思的动画,例如steps配合 paint 绘制时钟(注意paint的ctx对象不支持绘制文本)。
我是喜欢归纳总结前端相关知识的前端阿彬,尽力持续输出原创优质文章,欢迎点赞关注
往期文章:
# ☕ 通过和vue语法逐一比对,快速上手前端框架黑马svelte
# ♀️css魔法:伪元素content ➕ css函数
# 玩转css逐帧动画,纯css让哥哥动起来
# 2023 前端 SEO 无死角解读
# 我给自己搭建的前端导航网站,你们都别用
# 2023 最新最细 vite+vue3+ts 多页面项目架构,建议收藏备用!
# 浅谈 强制缓存/协商缓存 怎么用?
# 2023 前端性能优化清单