• CSS魔术师Houdini,用浏览器引擎实现高级CSS效果


    fbce5e79f3a74987b5dc1cd239a52e52.png

    开门见山,直接上货

    🔍 CSS Houdini是什么?

    “Houdini”一词引用自“Harry Houdini”,他是一位20世纪的著名魔术师,亦被称为史上最伟大的魔术师、逃脱术师及特级表演者。

    我们都知道,浏览器在渲染网页显示样式的时候,浏览器的渲染引擎会对 CSSOM 进行解析——包括布局、绘制和合成过程。而本文要讲的内容 Houdini一组公开 CSS 引擎部分的底层API,它可以让开发者能够人工干预浏览器的渲染进程,Houdini的核心API Painting API就是将我们自定义的js代码插入到浏览器的绘制环节。

    Houdini包括以下 API >>>

    • CSS Properties and Values API:顾名思义,就是与css属性相关的一系列 API,用于注册新的 CSS 属性,包括CSS.registerProperty API 和@property关键词(它们俩是等价的),例如我们常用 @property 常用于定义新的css属性,以实现一些css属性的动画过程,例如背景渐变色的变化过程。

    • CSS Typed OM:我们经常使用dom对象的style属性获取/设置它的一些 css 属性值,操作的值都是字符串。而 CSS Typed OM API为我们提供了一些列方法,可以获取/设置具体的css属性值、单位,(你可能觉得它很鸡肋,但是它也有它的优势,例如不用考虑属性的写法、强大的数学操作和单位转换、错误捕获处理、性能更好。

    • CSS Painting API:它可以说是CSS Houdini的核心 API之一,它其实就是css界的canvas,实现给元素设置复杂的自定义背景。使用时像 CSS.registerProperty 注册一个画像,然后就可以把这个画像应用于任何可以设置image的css属性上,例如background-imageborder-imagelist-style-image等。

    • Worklets工作集也是CSS Houdini的核心 API之一。它用于在独立于主要 JavaScript 执行环境的渲染管道的各个阶段运行脚本的 API。它相当于 Web Workers 渲染管道的轻量级版本。它只有一个实例方法Worklet.addModule(),用于将给定 URL 处的脚本模块添加到当前工作集。注册工作集后,你可以像任何其他值一样在 CSS 中使用它。

    • CSS Layout API、CSS Parser API、Font Metrics API:最后这三个 API目前浏览器支持性都几乎为零,所以本文就不做介绍了。

    ☕ CSS Houdini怎么用?

    CSS Houdini使用方法很简单,我总结为有以下几个步骤,我以实现一个波浪线来进行演示(最终效果如下图):

    image.png

    1. 创建工作集文件

    首先我们需要创建一个js文件,我这里取名为curved-line.js,我们要在里面实现自定义的背景效果。

    2.使用registerPaint注册工作集

    在我们创建的curved-line.js文件中添加以下代码注册工作集:
    registerPaint有两个参数:registerPaint(name, classRef),如下代码:

    //实现工作集的类的引用。
    class CurvedLine {
      //实现自定义css效果的代码
    }
    
    //注册Worklets工作集  "curved-line": 要注册的工作集类的名称
    registerPaint("curved-line", CurvedLine);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3.在工作集类中实现炫酷效果

    绘图工作集类我们需要关注以下 4 个函数:

    • contextOptions:该函数定义了一个上下文选项contextOptions(),如下面的例子中返回一个简单的对象,声明允许 alpha 透明度;
    • inputProperties,要使用哪些CSS自定义属性(只能使用var变量,不可以使用css内置属性);
    • inputArguments,CSS中使用paint函数除了模块名外的其他参数,指定其类型;
    • paint:最关键的方法,定义绘制行为。ctx的使用和canvas一致,size表示绘制的大小,包括width、height等信息,properties就是inputProperties静态方法里定义的属性,args就是paint的入参,跟inputArguments定义的对应。

    下面是这 4 个方法的使用示例:

    //file.js  工作集类方法示例
    registerPaint('paint-color-example', class {  
      static get inputProperties() {   
        return ['--my-color'];  
      }  
        
      static get inputArguments() {   
        return [''];  
      }  
        
      static get contextOptions() {   
        return {alpha: true};  
      }  
      
      paint(ctx, size, properties, args) {  
        ctx.fillStyle = properties.get('--my-color');  
        ctx.beginPath();  
        ...  
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    以下是绘制曲线的代码(不多说了,就是用canvas绘制曲线):

    // curved-line.js
    if (typeof registerPaint !== "undefined") {
      class CurvedLine {
        static get inputProperties() {
          return [
            "--curved-lineColor",
            "--curved-lineSpread",
            "--curved-lineWidth",
            "--curved-lineHeight",
          ];
        }
    
        paint(ctx, size, properties) {
          const lineWidth = parseInt(properties.get('--curved-lineWidth')) || 3;
          const lineHeight = parseInt(properties.get('--curved-lineHeight')) || size.height;
          const color = String(properties.get('--curved-lineColor')) || 'black';
          const spread = parseInt(properties.get('--curved-lineSpread')) || 50;
    
          const offset = (lineHeight < size.height) ? (size.height - lineHeight) / 2 : 0;
          const midPoint = lineHeight / 2;
    
          ctx.lineWidth = lineWidth;
          ctx.strokeStyle = color;
          ctx.beginPath();
          ctx.moveTo(0, midPoint + offset);
          
          let curStep = spread;
          while (curStep < size.width + spread) {
            const cp1x = curStep;
            const cp1y = (lineHeight * 1.5) + offset;
            const cp2x = curStep + spread;
            const cp2y = 0 - midPoint + offset;
            const x = curStep + spread * 2;
            const y = midPoint + offset;
    
            ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
            curStep = curStep + (spread * 3);
          }
          ctx.stroke();
        }
      }
    
      registerPaint("curved-line", CurvedLine);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    4. 将自定义的工作集添加到当前工作集

    你要知道想要实现 CSS Houdini,即从原生层面扩展 CSS,就必须要以Worklets工作集的方式将自定义css的代码注册到渲染引擎的进程中。上面我们也提到了Worklets的使用方法:使用 Worklet.addModule() 实例方法将指定 URL 处的脚本模块添加到当前工作集,如下:

    需要注意的是:

    • Worklets可以使用的类有很多种,例如网络音频工作集AudioWorklet,高性能程序动画工作集AnimationWorklet,绘图工作集PaintWorklet等,我们这里只使用了 PaintWorklet;
    • 工作集必须是个单独的js文件,并通过 Worklet.addModule() 引入;
    • Worklet 允许ESM静态导入,即使用import导入,但是不支持import()动态导入,会抛出异常!

    在index.html或者main.js中添加我们的工作集:

    <script>
      CSS.paintWorklet.addModule("curved-line.js");
    </script>
    
    • 1
    • 2
    • 3

    5. 使用paint()函数设置炫酷背景

    .line{
        width: 400px;
        height: 200px;
        --curved-lineHeight:20;
        --curved-lineWidth:4;
        --curved-lineColor: green;
        --curved-lineSpread:15;
        background: paint(curved-line);
    }
    
    <div class="line"></div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    大功告成~~~🎉🎉🎉

    🍭 CSS Houdini 的优点

    以往我们实现复杂的效果往往都需要使用js和css进行配合,而使用 Houdini 后,相比 JavaScript ,它能够更快的解析。因为 Houdini 的代码不会像js一样等待 cssom 布局绘制完成后然后又可能造成回流重绘,它是被注入到浏览器渲染引擎的渲染进程中的。

    在 JavaScript 中键入 CSS 值以及填充或发明新的 CSS 而不会影响性能终于成为可能。Houdini具有增强网络创造力的潜力。

    🍭 CSS Houdini 的兼容性

    目前 Houdini 的核心 API Painting API 兼容性还不是很好,使用时注意判断浏览器是否支持。

    image.png

    🎨 Houdini 案例

    以下网站有一些很有意思的 Houdini Demo,感兴趣的可以看下:
    https://houdini.how/

    思考:因为利用 paint 绘制图案是静态单次绘制,没有办法只通过工作集来实现连续的动画过程,但是可以配合animation进行多次渲染,尤其是 steps 逐帧动画,配合 Houdini ,可以创造很多有意思的动画,例如steps配合 paint 绘制时钟(注意paint的ctx对象不支持绘制文本)。

    最后

    我是喜欢归纳总结前端相关知识的前端阿彬,尽力持续输出原创优质文章,欢迎点赞关注😘

    表情包2.webp

    往期文章
    # ☕ 通过和vue语法逐一比对,快速上手前端框架黑马svelte
    # 🧙‍♀️css魔法:伪元素content ➕ css函数
    # 玩转css逐帧动画,纯css让哥哥动起来💃
    # 🕸2023 前端 SEO 无死角解读
    # 我给自己搭建的前端导航网站,你们都别用🤪
    # 2023 最新最细 vite+vue3+ts 多页面项目架构,建议收藏备用!
    # 浅谈 强制缓存/协商缓存 怎么用?
    # 2023 前端性能优化清单

  • 相关阅读:
    buuctf(探险3)
    linux安装kafka教程
    0 upgraded, 0 newly installed, 0 to remove and 112 not upgraded解决方法
    数据结构与算法5-栈
    NVIDIA 7th SkyHackathon(四)Nemo ASR 模型训练与评估
    类成员的访问控制属性
    PowerShell切换多个java版本
    word常用的文件格式有哪些?word格式文件删除了怎么恢复
    C语言结构体应用-通讯录
    【Java八股文总结】之Spring
  • 原文地址:https://blog.csdn.net/qq_38974163/article/details/132603877