一文理解 JS in CSS

CSS in JS是一种解决css问题想法的集合,它是将 CSS 样式写在 JavaScript 文件中,而不需要独立出.css.less之类的文件。将 CSS 放在 JS 中使我们更方便的使用 JS 的变量、模块化、tree-shaking。还解决了 CSS 中的一些问题,比如更方便解决基于状态的样式,更容易追溯依赖关系,生成唯一的选择器来锁定作用域。实现CSS in JS的第三方库有像 JSSstyled-components 等。

因此,JS in CSS 就是可以在 CSS 中使用 JavaScript 脚本,实现是基于 CSS Houdini

Houdini

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

 

CSS Houdini 新的API可以扩展 css 的变量,可以定义 CSS 变量的 类型,初始值,继承,比 CSS 变量更强大灵活。

Houdini 提供了两种自定义属性的注册方式,分别是在 JS 和 CSS 中。

CSS.registerProperty({
  name: '--my-prop', // String 自定义属性名
  syntax: '<color>', // String 如何去解析当前的属性,即属性类型,默认 *
  inherits: false, // Boolean 如果是true,子节点将会继承
  initialValue: '#c0ffee', // String 属性点初始值
});

或
@property --my-prop {
  syntax: '<color>';
  inherits: false;
  initial-value: #c0ffee;
}

还可以使用+使syntax属性支持一个或多个类型,也可以使用|来分割。更多syntax属性值: 

Worklets

Worklets 是渲染引擎的扩展,从概念上来讲它类似于 Web Workers,但有几个重要的区别:

  1. 设计为并行,每个 Worklets 必须始终有两个或更多的实例,它们中的任何一个都可以在被调用时运行
  2. 作用域较小,限制不能访问全局作用域的API(Worklet的函数除外)
  3. 渲染引擎会在需要的时候调用他们,而不是我们手动调用

Worklet 是一个 JavaScript 模块,通过调用 worklet 的addModule方法(它是个 Promise )来添加。比如  registerLayout,  registerPaint,  registerAnimator 都是需要放在 Worklet 中。

Worklets 的生命周期流程如下:

  • Worklet的生命周期从渲染引擎内开始;
  • 对于 JavaScript,渲染引擎启动 JavaScript 主线程;
  • 然后启动多个 workle t进程,并且可以运行。这些进程理想情况下是独立于主线程的线程,这样就不会阻塞主线程(但它们也不需要阻塞);
  • 然后在主线程中加载浏览器的 JavaScript;
  • 该 JavaScript 调用 worklet.addModule 并异步加载一个 worklet;
  • 加载后,将 worklet 加载到两个或多个可用的 worklet 流程中;
  • 当需要时,渲染引擎将通过从加载的 Worklet 中调用适当的处理函数来执行 Worklet。该调用可以针对任何并行的 Worklet 实例。

Typed OM

Typed OM是对现有的CSSOM的扩展,并实现 Parsing APIProperties & Values API相关的特性。它将 CSS 值转化为有意义类型的 JavaScript 对象,而不是像现在的字符串。。

现在读取 CSS 值增加了新的基类CSSStyleValue,有许多的子类可以更加精准的描述 CSS 值的类型:

 使用 Typed OM主要有两种方法:

  1. 通过 attributeStyleMap设置和获取有类型的行间样式;
  2. 通过 computedStyleMap获取元素完整的 Typed OM样式。

Layout

CSS Layout API 暴露了一个 registerLayout方法给开发者实现自己的布局算法,类似 display: flex 等,接收一个布局名(layout name)作为后面在 CSS 中使用的属性值,还有一个包含有这个布局逻辑的 JavaScript 类。

<style>
my-div {
  display: layout(my-layout);
}
</style>
<script> 
await CSS.layoutWorklet.addModule('layout-worklet.js');
</script>


/** layout-worklet.js */
registerLayout('my-layout', class {
  static get inputProperties() { return ['--foo']; }
  
  static get childrenInputProperties() { return ['--bar']; }
  
  async intrinsicSizes(children, edges, styleMap) {}

  async layout(children, edges, constraints, styleMap) {}
});

Painting

可以在 CSS background-image中使用 Painting API ,可以使用 Canvas 2d上下文,根据元素的大小控制图像,还可以使用自定义属性。

<script> 
await CSS.paintWorklet.addModule('paint-worklet.js');
</script>

/** layout-worklet.js */
registerPaint('sample-paint', class {
  static get inputProperties() { return ['--foo']; }

  static get inputArguments() { return ['<color>']; }

  static get contextOptions() { return {alpha: true}; }

  paint(ctx, size, props, args) { }
});

Animation

Animation API 可以控制基于用户输入的关键帧动画,并且以非阻塞的方式。还能更改一个 DOM 元素的属性,不过是不会引起渲染引擎重新计算布局或者样式的属性,类似 transform、opacity 或者滚动条位置(scroll offset)。Animation API的使用需要通过new一个WorkletAnimation来注册worklet:

<script>
await CSS.animationWorklet.addModule('animation-worklet.js');

// 需要添加动画的元素
const elem = document.querySelector('#my-elem');
const scrollSource = document.scrollingElement;
const timeRange = 1000;
const scrollTimeline = new ScrollTimeline({
  scrollSource,
  timeRange,
});

const effectKeyframes = new KeyframeEffect(
  elem,
  // 动画需要绑定的关键帧
  [
    {transform: 'scale(1)'},
    {transform: 'scale(.25)'},
    {transform: 'scale(1)'}
  ],
  {
    duration: timeRange,
  },
);
new WorkletAnimation(
  'sample-animator',
  effectKeyframes,
  scrollTimeline,
  {},
).play();
</script>

/** animation-worklet.js */
registerAnimator('sample-animator', class {
  constructor(options) {
  }
  animate(currentTime, effect) {
    effect.localTime = currentTime;
  }
});

Parser

允许开发者自由扩展 CSS 词法分析器。

解析规则:

const background = window.cssParse.rule("background: green");
console.log(background.styleMap.get("background").value) // "green"

const styles = window.cssParse.ruleSet(".foo { background: green; margin: 5px; }");
console.log(styles.length) // 5
console.log(styles[0].styleMap.get("margin-top").value) // 5
console.log(styles[0].styleMap.get("margin-top").type) // "px"

解析CSS:

const style = fetch("style.css")
        .then(response => CSS.parseStylesheet(response.body));
style.then(console.log);

Font Metrics

Font Metrics API 提供一些方法来测量在屏幕上呈现的文本元素的尺寸,将允许开发者控制文本元素在屏幕上呈现的方式。使用当前功能很难或无法测量这些值,例如:

  • flex布局: align-items baseline 特性。需要知道每一个 flex 盒子中第一个元素的基线位置。
  • 首字母: 需要知道每个字母的基线高度和字母最大的高度,以及换行内容的基线长度。
  • 单个字形的前进和后退。
  • 换行: 需要访问字体数据,文本的所有样式输入以及布局信息(可用的段落长度等)。
  • 元素中的每一个 line boxes都需要一个基线。(line boxes代表包含众多inline boxes的这行)

因此该API将使开发者可以更轻松地创建与文本和字体相关的 CSS 特性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

薛定谔的猫96

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值