Houdini
Css in JS、JS in CSS
Houdini API 介绍
现代的 Web
开发中,JavaScript
几乎占据所有版面,除了控制页面逻辑与操作 DOM
对象以外,连 CSS
都直接写在 JavaScript
里面了,就算浏览器都还沒有实现的特性,总会有人做出对应
的 Polyfills
,让你快速的将新 Feature
应用到 Production
环境中,还有 Babel
SWC
等工具负责转译。但是 CSS
就不同了,除了制定 CSS
标准规范所需的时间外,
各家浏览器的版本、实战进度差异更是旷日持久,顶多利用 PostCSS
、Sass
、Autoperfixer
这些前后处理器等工具来帮我们转译出浏览器能接受的 CSS
。开发者们能操作的就是通过
JS
去控制 DOM
与 CSSOM
来影响页面的变化,但是对于接下来的 Layout
、Paint
与 Composite
就几乎沒有控制权了。为了解決上述问题,为了让 CSS
的魔力不再浏览器把持,Houdini
就诞生了!( Houdini
也叫胡迪尼,是美国的伟大魔术师,擅长逃脱术,很适合想将 CSS
从浏览器中解放的概念)CSS Houdini
是由一群来自 Mozilla
, Safari
, Opera
,Microsoft
, HP
,
Intel
,IBM
,Adobe
与 Google
的工程师所组成的工作小组,志在建立一系列的 API
,让开发者能够介入浏览器的 CSS engine
。
让开发者对 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()
函
数,也可以使⽤刚刚注册好的名字。
<!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>
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)
那样的布局能⼒。
Worklets 的概念和 web worker 类似,它们允许你引入脚本文件并执行特定的 JS 代码,这样的 JS 代码要满足两个条件:
- 可以在渲染流程中调用;
- 第二,和主线程独立。
Worklet 脚本严格控制了开发者所能执行的操作类型,这就保证了性能 Worklets 的特点就是轻量以及生命周期较短。