前情提要
CSS: 老板, 你看 ES9,ES10 都出来了, 您看我的事情什么时候...
W3C: 这不是正在走着流程嘛! 小 C 你不要心急!
W3C:(语重心长)你看啊, 我们先 (1) 提个开发提案章程, 然后再批准成立工作组 (2) 紧接着工作组建立标准和指南, 然后再修改修改.(3)再然后啊, 给顾问委员会技术报告审核一下 (4) 最后浏览器厂商再实现一下这不就完事了吗? 大概不到 10 年咱们就全部搞定了吧
CSS: ...... (难过)
W3C:(顿了顿): 但是这些年啊, 你的刻苦努力我们都是看在眼里的!
CSS: 所以!!??(兴奋加期待)
W3C: 所以啊, 我们要感谢你为前端社区, 所作出的贡献!
CSS:......(沮丧)
W3C: 但是呢! 考虑到你设备老旧, 是该更新一下啦.
CSS: 请问是!?(兴奋加期待, 还有微微的迟疑)
W3C: 没错, 这是老板我送给你的步入成年的礼物 --Houdini
什么是 Houdini
Houdini 包含 6 组 API
S1. Paint API
简单地讲, Paint API 就是允许你通过 JavaScript 注册一个背景函数, 类似于 linear-gradient()那种, 在定义时候可以提供 Canvas 的 2Dcontext 给你自行绘制, 你通过 JS 注册这个背景函数后呢, 就可以自由地在 CSS 中通过以下方式使用该背景
background: paint(背景函数名);
如果想了解 Paint API 的 MDN 入门指导请点击这里 , 因为这个 MDN 上是有入门文档的, 所以就不放草案了
S2.Layout API
可以允许你自行定义布局, 并通过以下方式使用:
display:layout(自定义布局名)
这意味着什么呢? 我们常用的 flex 布局, 栅格布局都可以重新通过 display 进行定义和使用, 非常方便. 甚至, 如果 / 假如 Layout API 早出来 10 年的话, 我们甚至可以为这些类似于 flex 等的新兴属性定义 polyfill!, 就像我们为 ES6 中的 proxy 对象, promise 对象分别添加 proxy-polyfill,promise-polyfill 一样, 实现对低级浏览器的兼容, 这无疑让人感到高兴.
Layout 的定义虽然是为布局使用的, 但实际上, 根据 W3C 草案, 它还有控制定位和 overflow 等的能力, 总之, 除了动画意外的疑难杂症, Layout 几乎都能够涉足. 如果你想仔细了解: layout 的 W3C 草案 https://drafts.css-houdini.org/css-layout-api/
S3. AnimationWorklet
这么关键的场合怎么能少得了动画呢? 它可以控制动画的效果. 该 API 使用户可以在专有的线程中去运行动画, 从而大大降低了主线程的压力. 如果想了解 AnimationWorklet 的 W3C 草案内容请点击这里, 这个草案在今年的 6 月底刚刚发布
S4. Properties & Values
用于自定义 CSS 属性并为其约定类型, 行为和默认值. 可以看作 Less 的 @式定义和 Sass 的 $ 式定义的 2.0 加强版. 顺便一提, 它经常和前 3 个 API 搭配使用.
S5. Parser API
允许开发者自由扩展 CSS 词法分析器, 比如新的媒体规则, 新的伪类, 嵌套等等. 时至今日, 它还没有形成一个完整的草案
, 只是初具模型, 大家可以阅读 Parser API 草案(W3C) https://wicg.github.io/CSS-Parser-API/
S6.Typed OM
用人话解释就是, 原本我们不是可以通过 element.style.cssText 去修改 CSS 样式嘛, 大佬们觉得这样的字符串操作麻烦而且速度很慢, 所以定义了一组 CSS 专有的接口对象去进行操作, Typed OM 提供了与底层值交互的接口, 通过使用专门的 JS 对象来表示它们. 除此外还增加了一些其他的 API 点击这里查看 MDN 的 Typed OM 的入门指导
为什么说 Houdini 是 CSS 的成人礼?
这个问题等同于:
问题: 结合 CSS 的产生背景和历史渊源, 请问如何客观评价 Houdini 的历史地位?(本小题 10 分, 请考生答题时不要超出装订线) ( 难道我们在考历史题吗? 逃Σ( ° △ °|||)︴).
A. 为 CSS 新特性提供 polyfill
它的主要作用在于给予开发者更多开发 CSS 的自由度, 推动 CSS 新特性发布的进程, 同时为未来的那些像 flexbox 这样优秀的特性提供 polyfill, 让我们可以不再需要顾忌兼容性, 而能够尽快地使用新发布的 CSS 特性, 当然, CSS 的生态也会因此更为繁荣.
举个例子, 我们在使用 ES6 的 Promise 的时候, 怎么考虑让它在低版本浏览器不报 "Promise is undefined" 的错误呢? 很简单, 只要加个 promise-polyfill 就可以了, 如果你想一本万利, 那么导入个 babel-polyfill, 所有(严格的说是大部分)ES6 的新对象都可以放心用了.
试着想一下, 假如 Houdini 比 flex 早出来 10 年, 这个时候还需要担心 flex 兼容性吗? 不需要了, 我们可以从 NPM 社区上下一个包, 这个前人写好的 NPM 包注册了一个 layout 方法, 你只要下载这个包, 在 index 引用运行一下, 然后可以肆无忌怛的通过使用 flex 了, 想怎么浪就怎么浪
B. Houdini 的作用是为 CSS 提供进一步的完善
Houdini 出来是 16 年的事情了, 当时的话大家都觉得个这个新东西挺有发展潜力的, 但是我觉得嘛, 要客观看待, Houdini 做的事情, 其实很多时候, 原本我们就可以做了, 举个例子:
Paint API: 在 JS 领域里直接使用 Canvas 标签和包装起来的相关函数也是可以做的
Layout API: 提供的是大幅度的自定义布局的功能, 但问题是...flexbox 和 grid 已经把大多数场景给肝了. Layout 要是早出来还好, 这比 flex 完了这么多年, 就感觉有点尴尬
Typed OM: 提供了直接操作 CSS 属性的对象接口, 但问题是 CSSOM 的标准出来也不少时间了呀, 相比之下 Typed OM 的功能好像就失了一些新意...(某个表情包老头估计会说: 别笔笔, 一梭子 ele.style.cssText 不就完事了吗!)
其他, 想到再更
我们可以理解为, Houdini 在为一开始 CSS 没有设计想要, 并设计进去的一些东西做一些补充(XX 可能会迟到! 但永远不会缺席. 但问题是没打到卡是要扣工资的呀~)
CSS: 大师! 我感觉我在传承了 Houdini 的灵力加持后功力大增!
大师: 那尼玛是因为你一开始的内力...emmm
啊~ 下面又到了我最喜欢的互怼环节了呀
告别 CSS
为什么 CSS 一开始这么难学
前端工程师讨厌写 CSS 是什么心态?
为什么说 Houdini 是 CSS 漫长的成人礼?
至今为止, 刚刚在 can i use 上查到的结果显示, 目前 houdini 的七大特性, 除了 Paint API 和 TypedOM 外, 其余几个 API 在浏览器上可以说都是全军覆没的状态(或者说刚刚萌芽更合适一些?), 就算是 Paint API 也好, 它也仅仅只在 Edge76,Chrome66 和 Opera52 以上实现了技术扎根, 其余浏览器, 哪怕是 IE,Firefox,Safari 的最新版本, 也都尚未实现
下面这张是谷歌上搜到的, 2018 年底制作, 现在可以认为绝大部分仍然适用
可能有同学会问了, 上面写的 Chrome-Canary 是什么意思呢? 额...canary 直译为 "金丝雀", 属于和 dev,beta 相类似的版本的概念范畴,(你就理解为内测版吧, 逃~)根据相关资料提示, Layout API 在 Chrome-Canary 上实现部分支持, 但是我试用了一下发现还是用不了(心塞), 控制台 CSS.layoutWorklet 打出来是 undefined, 所以评论区有高人还请指教下啦~
Chrome-Canary 中国区下载链接
实战 Houdini 之 Paint API
那咱们就写一个咱们可以在 Chrome 上跑的 demo 好了
首先, 我们需要编写一个 JavaScript 文件, 我们命名为 paint.JS, 然后通过 registerPaint 方法注册一个 paint 方法, paint 方法可以绘制 div 的背景, 例如下面我们把这个 paint 方法命名为 circle, 这意味着它可以通过 background: paint(circle)去使用绘制的画布. 我们上面也说过, 我们注册 paint 方法时系统会提供 ctx 作为参数, 这个 ctx 是 html5/Canvas 的 2Dcontext 的子集, 实现了除了文字操作外的大多数方法和属性.(文字操作指的是 ctx.fillText 或者 ctx.strokeText 这一类方法).
下面我们来通过 JS 注册一个 paint 方法, 来为 div 添加一个 background, 这个 paint 方法命名为 circle, 它的功能是为 div 提供一个半径为 Math.min(长, 宽)的实心圆, 圆的背景色可在 CSS 的上下文代码块中通过 --color 这个属性名指定.
paint.JS 的全部代码如下
- // paint.JS
- registerPaint('circle', class {
- // 决定了 paint 方法中 props 中能获取的 CSS 属性值
- static get inputProperties() { return ['--color']; }
- // 绘制一个半径为长或宽的最小值的圆形作为背景
- // ctx 是 Canvas 的 ctx 的子集, 实现了除文字操作外的大多数方法和属性
- paint(ctx, size, props) {
- // size 表示使用该 paint 方法的 div 的长和宽
- const width = size.width / 2;
- const height = size.height / 2;
- const radius = Math.min(width, height);
- // props.get 表示将根据 inputProperties 返回的键值从 CSS 代码块中获取相应属性
- const color = props.get('--color');
- // 给画笔着色
- ctx.fillStyle = color;
- // 开始动笔绘制
- ctx.beginPath();
- // 以 width,height 为圆心, radius 为半径画圆圈, 从 0 度画到 360 度
- ctx.arc(width, height, radius, 0, 2 * Math.PI);
- // 用 fillstyle 把圆圈轨迹内部进行颜色填充
- ctx.fill();
- // 搞定!
- }
- });
- (代码中的具体 API 我们过会再仔细解释, 我们先把代码跑通再说)
然后, 仅仅这样做是不够的, 我们还需要在主线程里, 例如 main.HTML 的脚本里. 通过下面这个 API 去加载我们刚刚定义的 paint.JS
CSS.paintWorklet.addModule('paint.js');
注意, paint.JS 内部会形成一个封闭而独立的, 叫 worklet 的作用域, 它和全局 Windows 是不一样的! 不要在这里尝试使用 fetch 等方法.
上面两步做完了, 我们就可以使用 CSS 去使用我们刚刚定义的 paint 函数了
- <!-- HTML -->
- <div class="foo"></div>
- <!-- CSS -->
- <style>
- .foo {
- border: 1px solid blue;
- width: 200px;
- height: 200px;
- /* 指定背景色为红色 */
- --color: red;
- /* 使用刚刚注册的 paint 方法 */
- background-image: paint(circle);
- }
- </style>
我们看下效果
GitHub 项目代码如下
https://github.com/penghuwan/houdini-module
好, 我们回过头来解释下 paint 方法里面的逻辑
(1)size 它是一个对象, 保存了使用这个定义 paint 方法的 div 的长和宽, 可以分别通过 size.width 和 size.height 去访问
- registerPaint('circle', class {
- // ctx 是 Canvas 的 ctx 的子集, 实现了除文字操作外的大多数方法和属性
- paint(ctx, size, props) {
- // size 表示使用该 paint 方法的 div 的长和宽
- const width = size.width / 2;
- const height = size.height / 2;
- // ...
- }
- });
(2)props: 我们可以通过 props 参数获取 CSS 上下文代码块 (指的是 paint(circle) 在的那个代码块)的其他 CSS 属性, 但是这个属性需要在 inputProperties 函数中进行声明, 声明的方法是在函数中返回一个数组, 数组项为属性名称
- // paint.JS
- registerPaint('circle', class {
- // 决定了 paint 方法中 props 中能获取的 CSS 属性值
- static get inputProperties() { return ['--color']; }
- // ctx 是 Canvas 的 ctx 的子集, 实现了除文字操作外的大多数方法和属性
- paint(ctx, size, props) {
- const color = props.get('--color');
- }
- });
- // style
- .foo {
- /* 指定背景色为红色 */
- --color: red;
- /* 使用刚刚注册的 paint 方法 */
- background-image: paint(circle);
- }
(3)ctx: 这个参数上文已经多次介绍, 这里不再赘述
警告, 下面讲的都是当前没有任何 stable 浏览器可以运行的代码, 我是根据 W3C 的草案和示范代码要求来的, 正所谓 -- 姜太公写代码, 愿者 Debug
- Layout
- <抱歉写的太累了, 休息下, 本部分待会再更>
- Animation
- <抱歉写的太累了, 休息下, 本部分待会再更>
Propertis 和 values
- <抱歉写的太累了, 休息下, 本部分待会再更>
- Typed OM
- <抱歉写的太累了, 休息下, 本部分待会再更>
CSS! 你的 Houdini 来啦
但是你用不了, 哈哈哈哈!
来, 我们换一个老司机的版本看看
CSS 小姐姐! 你 daisuki 的(最喜欢)Houdini 来了哟!
但是你用不了, 哈哈哈! 哈哈哈... 啊!?..... 啊雷雷?
本文完
参考资料
Houdini:CSS 领域最令人振奋的革新
Chrome 提供的 Houdini Demo
1 中 Demo 的在线运行地址(注意很多是暂时跑不了的)
layout 的 API 草案(W3C)
Painting API 的入门指导
Parser API 草案(W3C)
AnimationWorklet 工作草案(W3C)
给大家一点点建议, 谷歌英文资料吧... 很多干货
来源: https://www.cnblogs.com/penghuwan/p/11407053.html