CSS Houdini教程

朋友们,美好的一天!

什么是Houdini?


Houdini(Houdini)-浏览器API的集合,可以显着改善Web开发过程,包括CSS标准的开发。开发人员将能够使用JavaScript扩展CSS,从而影响CSS渲染并告诉浏览器应如何应用样式。与使用多聚体相比,这将显着提高性能和稳定性。

Houdini由两个API组组成-高级API和低级API。

高级API与呈现过程(样式-布局-绘图-合成)相关联。该组包括:

  • Paint API-允许您在渲染视觉元素(颜色,背景,边框等)的步骤(即渲染阶段)扩展CSS。
  • 布局API-允许您在确定元素的大小,位置和对齐方式的步骤中扩展CSS。
  • 动画API-显示和设置元素动画的步骤中的“扩展点”。

低级API是高级API的基础,包括:

  • 类型对象模型API
  • 自定义属性和值API
  • 字体指标API
  • 工作台

CSS的未来


Houdini与常规CSS不同,它允许开发人员以更自然的方式扩展CSS。这是否意味着CSS规范将停止发展并采用新的标准?一点也不。Houdini的目标是通过创建可以轻松标准化的工作原型来帮助开发CSS新功能。

此外,开发人员可以轻松共享打开的CSS工作集,而无需担心兼容性。

类型对象模型(TOM)API


在Houdini之前,JS和CSS交互的唯一方法是将CSS转换为字符串并对其进行修改。由于需要对值类型进行两次转换(例如,从数字到字符串,反之亦然),因此手动解析和重新定义样式可能很复杂且容易出错。您还必须手动指定新值的度量单位。

selectedElement.style.fontSize = newFontSize + 'px' // newFontSize = 20
console.log(selectedElement.style.fontSize) // 20px

通过将CSS属性表示为类型化的JS对象,TOM为CSS属性赋予了更多语义含义。这大大提高了性能,稳定性并促进了代码支持。CSS值由CSSUnitValue接口表示,该接口由值和``度量单位''属性组成。

{
  value: 20,
  unit: 'px'
}

该界面可用于以下新功能:

  • computeStyleMap():用于解析计算(非内置)样式。在解析或使用其他方法之前,将调用此方法。
  • attributeStyleMap:用于解析和修改内联样式。这是一个项目属性。

//       ( )
selectedElement.computedStyleMap().get('font-size') // { value: 20, unit: 'px' }

//   
selectedElement.attributeStyleMap.set('font-size', CSS.em(2))
selectedElement. attributeStyleMap.set('color', 'blue')

//    
selectedElement.computedStyleMap().get('font-size') // { value: 20, unit: 'px' }

//    
selectedElement.attributeStyleMap.get('font-size') // { value: 2, unit: 'em' }

请注意分配新值时如何使用CSS类型。使用此语法可以避免与类型相关的问题,并且生成的代码变得更加安全。

正在考虑的API不仅包括get和set,还包括其他方法,例如:

  • 清除:删除所有内联样式
  • delete:删除特定的CSS属性及其值
  • 具有:根据指定属性的可用性返回true / false
  • append:向支持多个值的属性添加额外的值

检测


let selectedElement = document.getElementById('example')

if(selectedElement.attributeStyleMap){
  // ...
}

if(selectedElement.computedStyleMap){
  // ...
}

规格状态


工作草案:发布以供社区讨论。

支持


边缘歌剧火狐浏览器苹果浏览器
支持者支持者支持者不支持部分支持

自定义属性和值API


该API允许开发人员通过定义类型,初始值和继承来扩展CSS变量。为了定义自定义属性,必须使用registerProperty方法注册它。此方法确定浏览器应如何应用属性和处理错误。

CSS.registerProperty({
  name: '--colorPrimary',
  syntax: '<color>',
  inherits: false,
  initialValue: 'blue',
})

具有以下属性的对象将作为参数传递给此方法:

  • 名称:自定义属性的名称
  • 语法:解析指令。预定义值是:<颜色>,<整数>,<数字>,<长度>,<百分比>等。
  • initialValue:默认值(覆盖之前以及发生错误时)

在上面的示例中,定义了<color>类型的自定义属性。此属性将用于确定渐变。常规CSS不支持渐变过渡。注意,如何使用custom属性定义过渡。

.gradient-box {
  background: linear-gradient(45deg, rgba(255, 255, 255, 1) 0% var(--colorPrimary) 60%);
  transition: --colorPrimary 0.5s ease;
  ...
}

.gradient-box:hover {
  --colorPrimary: red;
  ...
}

浏览器不知道如何进行渐变过渡,但知道如何进行颜色过渡。这就是为什么我们将属性类型定义为<color>的原因。在支持Houdini的浏览器中,将鼠标悬停时将发生渐变更改。也可以使用CSS自定义属性(注册为<percentage>)来更改以百分比为单位的渐变位置。

将来可能会在CSS中直接注册自定义属性。

@property --colorPrimary {
  syntax: '<color>';
  inherits: false;
  initial-value: blue;
}


这个简单的示例说明了如何使用自定义CSS属性更改渐变的颜色和控制点。可以在此处找到示例代码



检测


if(CSS.registeredProperty) {
  // ...
}

规格状态


工作草案:发布以供社区讨论。

支持


边缘歌剧火狐浏览器苹果浏览器
支持者支持者支持者不支持不支持

字体指标API


该API处于开发的早期阶段,因此该规范将来可能会发生巨大变化。当前,它提供了用于测量屏幕上显示的文本元素大小的方法,从而允许开发人员影响字符的呈现。现有的CSS功能不允许您使用这些值或使这项工作非常困难。使用此API的一个示例是文本的多行动态截断。

规格状态


意见征集:草稿未发表。

不支持浏览器。

Vorkleta


在继续使用以下API之前,您需要了解Worklet的概念。Vorklet是在渲染期间运行的脚本,并且独立于基础JS代码。它们扩展了渲染引擎的功能,专为并行执行(2个或更多实例)而设计,不阻塞主线程,对全局范围的访问受限,并且在必要时由引擎调用。Vorklet只能通过HTTPS(生产环境)或localhost(出于开发和测试目的)运行。

Houdini包含以下扩展浏览器呈现引擎的工作集:

  • Paint Worklet-Paint API
  • 动画作品-动画API
  • 布局工作簿-布局API

绘画API


Paint API允许开发人员使用JS函数通过2D渲染上下文(它是HTML5 Canvas API的子集)来绘制元素的背景,边框或内容。Paint API使用Paint Worklet绘制图像,该图像取决于CSS的更改(例如CSS变量的更改)。那些熟悉Canvas API的人将会对Paint API感到宾至如归。

创建Paint Worklet包含几个步骤:

  1. 使用registerPaint函数编写和注册工作集
  2. 使用CSS.paintWorklet.addModule调用HTML或JS中的工作表
  3. 在CSS中使用paint()方法以及vorklet的名称和传递的参数

让我们看一下registerPaint函数,该函数用于注册和确定Paint Worklet的功能。

registerPaint('paintWorkletExample', class {
  static get inputProperties() { return ['--myVariable']; }
  static get inputArguments() { return ['<color>']; }
  static get contextOptions() { return {alpha: true} }

  paint(ctx, size, properties, args) {
    // ...
  }
})

registerPaint函数由以下部分组成:

  • inputProperties:工作项正在观察的自定义CSS属性的数组。该数组表示工作项的依赖关系
  • inputArguments:可以从函数传递到外部CSS的参数数组
  • contextOptions:颜色透明度。如果为假,则所有颜色将完全不透明
  • paint:主要功能,采用以下参数:

    • ctx:2D绘图上下文,几乎与Canvas API的2D画布上下文相同
    • size:具有元素宽度和高度的对象。值取决于布局渲染过程。画布大小与实际商品大小相同
    • 属性:inputProperties中包含的变量
    • args:传递给paint函数的参数数组

注册工作文件后,必须以HTML调用它,指示文件的路径。

CSS.paintWorklet.addModule('path/to/worklet/file.js')

可以从任何外部来源(例如,从CDN)添加Vorklet,这使其成为模块化且可重复使用。

CSS.paintWorklet.addModule('https://url/to/worklet/file.js')

调用工作件后,可以使用“ paint”功能在CSS中使用它。此函数作为第一个参数,采用vorklet的注册名称以及在inputArguments中指定的所有参数。从这一刻起,浏览器就知道何时调用工作集以及哪些用户操作导致CSS自定义属性的某些值发生更改。

.example-element {
  // paintWorkletExample -  
  // blue - ,  
  background: paint(paintWorkletExample, blue);
}


下面的示例演示了Paint API的用法以及工作集的模块化和重用。它使用Google Chrome Labs存储库中的涟漪工作表在这里查看示例代码



检测


if(‘paintWorklet’ in CSS){
  // …
}

@supports(background: paint(paintWorkletExample)){
  // …
}

规格状态


建议:稳定的工作草案,随时可用。

支持


边缘歌剧火狐浏览器苹果浏览器
支持者支持者支持者不支持不支持

动画API


该API通过处理各种事件(滚动,悬停,单击等)扩展了Web动画,并通过动画工作集以自己的流形式启动动画来提高性能。

像其他任何工作区一样,必须首先注册锻炼动画。

registerAnimation(‘animationWorkletExample’, class {
  constructor(options){
    // …
  }
  animate(currentTime, effect){
    // …
  }
})

此类包含两个功能:

  • 构造函数:在创建新实例时调用。用于常规设置。
  • 动画:包含动画逻辑的主要功能。接受以下参数:

    • currentTime:特定时间轴上的时间戳
    • 效果:动画中使用的一系列效果

注册后,该工作集将包含在主JS文件中,并添加了动画(元素,框架,设置)并将其附加到时间轴上。下一节将介绍时间轴标记的概念和网络动画的基础。

//   
await CSS.animationWorklet.addModule(‘path/to/worklet/file.js’)

//    
const elementExample = document.getElementById(‘element-example’)

//  
const effectExample = new KeyframeEffect(
  elementExample, //  
  [ // … ], //  
  { // … } //  - , ,    ..
)

//        
new WorkletAnimation(
  ‘animationWorkletExample’ //  
  effectExample, //  
  document.timeline, //  
  {},  //   
).play()

时标


Web动画基于时间戳-动画时间表上效果的里程碑。例如,我们分析了一个重复的线性动画,该动画由三个帧(开始,中间,结尾)组成,在页面完全加载(延迟)后1秒开始,持续4秒。

效果的时间戳将如下所示(对于持续4秒且没有延迟的动画):

时标动画框架
0毫秒第一帧-动画的开始
2000毫秒第二帧-动画的中间
4000毫秒最后一帧-动画结束或重置为第一帧

值为3000ms(给定延迟为1000ms)的effect.localTime会将动画绑定到时间轴上的平均帧(1000ms延迟+ 2000ms平均帧)。设置7000ms和11000ms时将获得相同的效果,因为动画每4000ms重复一次。

animate(currentTime, effect){
  effect.localTime = 3000 // 1000ms  + 2000ms  
}

使用effect.localTime的恒定值,动画将锁定在特定帧上。因此,effect.localTime的值应更改。该值应该是绑定到currentTime或另一个变量的函数。

线性动画代码如下所示:

animate(currentTime, effect){
  effect.localTime = currentTime // y = x  
}

时间轴(document.timeline)时间戳记
startTime + 0ms(经过的时间)startTime + 0ms首先
startTime + 1000ms(经过的时间)startTime + 1000ms(延迟)+ 0ms首先
startTime + 3000ms(经过的时间)开始时间+ 1000毫秒(延迟)+ 2000毫秒中间
startTime + 5000ms(经过的时间)开始时间+ 1000毫秒(延迟)+ 4000毫秒最后/第一
startTime + 7000ms(经过的时间)开始时间+ 1000毫秒(延迟)+ 6000毫秒中间
startTime + 9000ms(经过的时间)开始时间+ 1000毫秒(延迟)+ 8000毫秒最后/第一

时间戳不限于1:1。动画API允许开发人员使用标准JS函数创建复杂的效果,通过动画函数来操纵标记。动画也可以在每次迭代中变化(具有可重复的动画)。

动画不仅可以与文档加载相关联,还可以与用户操作相关联。可以通过ScrollTimeline对象在动画中使用诸如滚动页面之类的用户操作。例如,动画可以在滚动200像素时开始,而在滚动800像素时结束。

const scrollTimelineExample = new ScrollTimeline({
  scrollSource: scrollElement, // ,     
  orientation: ‘vertical’, //  
  startScrollOffset: ‘200px’, //  
  endScrollOffset: ‘800px’, //  
  timeRange: 1200, //  
  fill: ‘forwards’ //  
})

动画会自动适应滚动速度,同时保持平滑和响应。由于动画在其自己的流中运行并连接到浏览器渲染引擎,因此动画可以平稳启动并且不会影响性能。


下面的示例演示非线性动画。它使用高斯函数,并在此前后同时旋转。在这里查看示例代码



检测


if(CSS.animationWorklet){
  // …
}

规格状态


第一份公共工作草案:准备进行社区讨论,将来可能会发生变化。

支持


边缘歌剧火狐浏览器苹果浏览器
部分支持部分支持部分支持不支持不支持

布局API


Layout API允许开发人员通过定义要在CSS“ display”属性中使用的新模块来扩展布局渲染过程。该API引入了新概念,非常复杂,并且提供了大量设置来开发用于处理页面布局的自定义算法。

首先,需要注册一个Worklet。

registerLayout(‘exampleLayout’, class{
  static get inputProperties() { return [‘--example-variable’] }

  static get childrenInputProperties() { return [‘--exampleChildVariable’] }

  static get layoutOptions(){
    return {
      childDisplay: ‘normal’,
      sizing: ‘block-like’
    }
  }

  intrinsicSizes(children, edges, styleMap){
    // …
  }

  layout(children, edges, constraints, styleMap, breakToken){
    // …
  }
})

注册工作簿包括以下方法:

  • inputProperties:工作集正在观察并属于父级的自定义CSS属性的数组,父属性是导致布局呈现的元素。此数组表示布局依赖性
  • childrenInputProperties:小部件监视的属于后代的自定义CSS属性的数组
  • layoutOptions:定义以下布局属性:

    • childDisplay:预定义的值是block和normal。定义项目的显示方式(块或行)
    • 大小:预定义值是块状和手动的。确定是否需要初步计算元素大小(如果未指定)
  • nativeSizes:定义如何在布局上下文中显示容器或其内容:

    • children:导致页面布局呈现的元素的子元素
    • 边缘:容器边界
    • styleMap:键入的容器样式对象模型
  • layout:使用布局的主要功能:

    • 子元素:子元素
    • 边缘:边框
    • 约束:父布局施加的约束
    • styleMap:键入的容器样式对象模型
    • breakToken:分页或打印布局拆分的断点

接下来,将工作项添加到HTML或JS文件。

CSS.layoutWorklet.addModule(‘path/to/worklet/file.js’)

我们用样式链接到文件中的工作项。

.example-element {
  display: layout(exampleLayout)
}

布局API如何与布局一起使用


在前面的示例中,我们定义了exampleLayout。

.example-element称为父级布局,包括缩进,边框和滚动滑块。父布局由称为当前布局的子元素组成。当前布局是目标元素,其布局是使用Layout API“自定义”的。例如,当使用“ display:flex”时,元素的后代会根据灵活的布局进行重新排列。这类似于Layout API的操作。

当前的每个布局都包含子布局,这些子布局包含用于渲染子元素LayoutChild的算法(包括伪类:: before和:: after)。LayoutChild-由CSS生成的容器,其中包含样式数据(不包含布局数据)。浏览器会在应用样式阶段自动创建LayoutChild元素。子布局可以创建一个Fragment,其中包含用于渲染布局的指令。


此示例还使用了Google Chrome Labs存储库,但是文本被替换为图像。在这里查看示例代码



检测


if(CSS.layoutWorklet){
  // …
}

规格状态


第一份公共工作草案:准备进行社区讨论,将来可能会发生变化。

支持


边缘歌剧火狐浏览器苹果浏览器
部分支持部分支持部分支持不支持不支持

胡迪尼和不断进步


尽管Houdini当前没有最佳的浏览器支持,但可以将其用于逐步改进。如果您不熟悉渐进式改进的概念,建议您阅读本文使用Houdini时,应考虑以下几点:

始终确定支持以避免错误。


每个Houdini API和Worklet都有一种检查可用性的简便方法。这样可以避免在尚不支持该技术的浏览器中使用Houdini的问题。

使用Houdini改善显示和可视化效果。


使用不支持Houdini的浏览器的用户应有权访问该网站的内容和基本功能。用户体验和内容显示不应依赖Houdini。

使用标准CSS作为替代


例如,自定义CSS属性可以用作自定义属性和值API的替代方法。

专注于开发生产和可靠的应用程序,将Houdini用于装饰目的只是一种渐进的改进。

结论


Houdini允许开发人员在渲染过程中使用JS代码处理样式,从而提高应用程序性能和稳定性。在呈现过程中嵌入代码的功能使您可以创建易于与他人共享,应用甚至可能包含在规范中的CSS多义词。此外,Houdini允许开发人员和设计人员在使用样式,布局和动画时减少对CSS限制的依赖。

Houdini现在可以使用,但只能作为逐步的改进。这将允许不支持Houdini的浏览器显示页面而不会出现错误,从而提供最佳的用户体验。

我等不及开发人员社区才能充分享受Houdini的功能。以下是几个示例:

CSS Houdini过期CSS Houdini
交互式介绍Houdini
Google Chrome实验室的示例

参考文献


Houdini
Houdini 规范的W3C草案(Chrome Dev Summit 2018)
动画工作集-Google Developers

感谢您的关注。

All Articles