Skip to main content

Houdini

Css in JS、JS in CSS

Houdini API 介绍

现代的 Web 开发中,JavaScript 几乎占据所有版面,除了控制页面逻辑与操作 DOM 对象以外,连 CSS 都直接写在 JavaScript 里面了,就算浏览器都还沒有实现的特性,总会有人做出对应 的 Polyfills,让你快速的将新 Feature 应用到 Production 环境中,还有 Babel SWC 等工具负责转译。但是 CSS 就不同了,除了制定 CSS 标准规范所需的时间外, 各家浏览器的版本、实战进度差异更是旷日持久,顶多利用 PostCSSSassAutoperfixer 这些前后处理器等工具来帮我们转译出浏览器能接受的 CSS。开发者们能操作的就是通过 JS 去控制 DOMCSSOM 来影响页面的变化,但是对于接下来的 LayoutPaintComposite 就几乎沒有控制权了。为了解決上述问题,为了让 CSS 的魔力不再浏览器把持,Houdini 就诞生了!( Houdini 也叫胡迪尼,是美国的伟大魔术师,擅长逃脱术,很适合想将 CSS 从浏览器中解放的概念)CSS Houdini 是由一群来自 MozillaSafariOperaMicrosoftHPIntelIBMAdobeGoogle 的工程师所组成的工作小组,志在建立一系列的 API,让开发者能够介入浏览器的 CSS engine

CSS Houdini 核心点

让开发者对 CSS engine 有足够的控制权,让我们把 JS 写入 CSS

CSS Parser

CSS Parser API 还没有被写⼊规范,所以下⾯我要说的内容随时都会有变化,但是它的基本思想不会变:允许开发者⾃由扩展 CSS 词法分析器,引⼊新的结构(constructs),⽐如新的媒体规则、新的伪 类、嵌套、 @extends 、@apply 等等。 只要新的词法分析器知道如何解析这些新结构,CSSOM 就不会直接忽略它们,⽽是把这些结构放到正确的地⽅。

CSS Panit

CSS Paint API Layout API ⾮常相似。它提供了⼀个 registerPaint ⽅法,操作⽅式和 registerLayout ⽅法也很相似。当想要构建⼀个 CSS 图像的时候,开发者随时可以调⽤ paint() 函 数,也可以使⽤刚刚注册好的名字。

HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>CSS Houdini绘制星空</title>
<style>
body {
margin: 0;
color: #fff;
font-size: 24px;
background: #000;
}

body::before {
content: "";
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
--star-density: 0.8;
--star-opacity: 1;
background-image: paint(sky);
animation: shine 1s linear alternate infinite;
}

@keyframes shine {
from {
--star-opacity: 1;
}

to {
--star-opacity: 0.4;
}
}
</style>
</head>

<body>
<script>
if (!CSS in window || !CSS.paintWorklet) {
console.log("不支持paintWorklet");
} else {
CSS.paintWorklet.addModule("./sky.js");
}
</script>
</body>
</html>
JS
class StarSky {
constructor() {}
static get inputProperties() {
return ["--star-density", "--star-opacity"];
}
paint(ctx, geom, properties) {
const xMax = geom.width;
const yMax = geom.height;
console.log(xMax, yMax);
ctx.fillRect(0, 0, xMax, yMax);
let starDensity = properties.get("--star-density").toString() || 1;
let starOpacity = properties.get("--star-opacity").toString() || 1;
const stars = Math.round((xMax + yMax) * starDensity);
for (let i = 0; i <= stars; i++) {
const x = Math.floor(Math.random() * xMax + 1);
const y = Math.floor(Math.random() * yMax + 1);
const size = Math.floor(Math.random() * 2 + 1);
const opacityOne = Math.floor(Math.random() * 9 + 1);
const opacityTwo = Math.floor(Math.random() * 9 + 1);
const opacity = +("." + (opacityOne + opacityTwo)) * starOpacity;
const hue = Math.floor(Math.random() * 360 + 1);
ctx.fillStyle = `hsla(${hue},30%,80%,${opacity})`;
ctx.fillRect(x, y, size, size);
}
}
}

registerPaint("sky", StarSky);

CSS Layout

CSS Layout API 允许开发者可以通过 CSS Layout API 实现⾃⼰的布局模块 (layout module),这⾥的 “ 布局模块 ” 指的是 display 的属性值。也就是说,这个 API 实现以后, 开发者⾸次拥有了像 CSS 原⽣代码 (⽐如 display : flex 、display:table) 那样的布局能⼒。

Worklet

Worklets 的概念和 web worker 类似,它们允许你引入脚本文件并执行特定的 JS 代码,这样的 JS 代码要满足两个条件:

  1. 可以在渲染流程中调用;
  2. 第二,和主线程独立。

Worklet 脚本严格控制了开发者所能执行的操作类型,这就保证了性能 Worklets 的特点就是轻量以及生命周期较短。