我们知道,浏览器在渲染页面时,首先会解析页面的 HTML 和 CSS,生成渲染树(rendering tree),再经由布局(layout)和绘制(painting),呈现出整个页面内容。在 Houdini 出现之前,这个流程上我们能操作的空间少之甚少,尤其是 layout 和 painting 环节,可以说是完全封闭,使得我们很难通过 polyfill 等类似的手段为欠支持的 CSS 特性提供兼容。而另一方面,语法特性的缺失也极大地限制了 CSS 的编程灵活性,社区中 sass、less、stylus 等 CSS 预处理技术的出现大多都源于这个原因,它们都希望通过预编译,突破 CSS 的局限性,让 CSS 拥有更强大的组织和编写能力。所以慢慢地,我们都不再手写 CSS,更方便、更灵活的 CSS 扩展语言成了 web 开发的主角。看到这样的情况,CSS Houdini 终于坐不住了。

什么是 CSS Houdini?

CSS Houdini 对外开放了浏览器解析流程的一系列 API,这些 API 允许开发者介入浏览器的 CSS engine 运作,带来了更多的 CSS 解决方案。

CSS Houdini 目前主要提供了以下几个 API:

CSS Properties and Values API

允许在 CSS 中定义变量和使用变量,是目前支持程度最高的一个 API。CSS 变量以 -- 开头,通过 var()调用:

div {--font-color: #9e4a9b;color: var(--font-color);
}

此外,CSS 变量也可以在其他节点中使用,只不过是有作用域限制的,也就是说自身定义的 CSS 变量只能被自身或自身的子节点使用:

.container {--font-color: #9e4a9b;
}
.container .text {color: var(--font-color);
}
web前端开发学习Q-q-u-n:784783012 ,分享学习的方法和需要注意的小细节,不停更新最新的教程和学习方法
(学习工具,详细的前端项目实战教程,PDF

定义和使用 CSS 变量可以让我们的 CSS 代码变得更加简洁明了,比如我们可以单纯通过改变变量来改变 box-shadow 的颜色:

.text {--box-shadow-color: #3a4ba2;box-shadow: 0 0 30px var(--box-shadow-color);
}
.text:hover {--box-shadow-color: #7f2c2b;
}

Painting API

允许开发者编写自己的 Paint Module,自定义诸如 background-image 这类的绘制属性。自定义的重点在于,“怎么画” 的逻辑需要我们来描述,因此我们利用 registerPaint 来描述我们的绘制逻辑:

registerPaint('rect', class {paint(ctx, size, properties, args) {// @TODO}
});

registerPaint 方法注册了一个 Paint 类 rect 以供调用,这个类的核心在于它的 paint 方法。paint 方法用于描述自定义的绘制逻辑,它接收四个参数:

  • ctx:一个 Canvas 的 Context 对象,因此 paint 中的绘制方式跟 canvas 绘制是一样的。
  • size:包含节点的尺寸信息,同时也是 canvas 可绘制范围(画板)的尺寸信息。
  • properties:包含节点的 CSS 属性,需要调用静态方法 inputProperties 声明注入。
  • args: CSS 中调用 Paint 类时传入的参数,需要调用静态方法 inputArguments 声明注入。

编写完 Paint 类之后,我们在 CSS 中只需要这样调用,就能应用到我们自定义的绘制逻辑:

.wrapper {background-image: paint(rect);
}

Painting API 目前在高版本 Chrome、Opera 浏览器已有支持,且实现起来比较简单,后边我们还将通过 demo 进一步演示。

Layout API

允许开发者编写自己的 Layout Module,自定义诸如 display 这类的布局属性。同样的,“如何布局” 的逻辑需要我们自己编写:

registerLayout('block-like', class {layout(children, edges, constraints, properties, breakToken) {// @TODOreturn {// inlineSize: number,// blockSize: number,// autoBlockSize: number,// childFragments: sequence<LayoutFragment>}}
})
web前端开发学习Q-q-u-n:784783012 ,分享学习的方法和需要注意的小细节,不停更新最新的教程和学习方法
(学习工具,详细的前端项目实战教程,PDF

registerLayout 方法用于注册一个 Layout 类以供调用,它的 layout 方法用于描述自定义的布局逻辑,最终返回一个包含布局后的位置尺寸信息和子节点序列信息的对象,引擎将根据这个对象进行布局渲染。

同样的,调用时只需:

.wrapper {display: layout('block-like');
}

因此利用 Layout API,你完全可以实现对 flex 布局的手工兼容。相比 Painting,Layout 的编写显得更加复杂,涉及到盒模型的深入概念,且支持度不高,这里就不细讲了。

Worklets

registerPaint、registerLayout 这些 API 在全局上并不存在,为什么可以直接调用呢?这是因为上述的 JS 代码并不是直接执行的,而是通过 Worklets 载入执行的。Worklets 类似于 Web Worker,是一个运行于主代码之外的独立工作进程,但比 Worker 更为轻量,负责 CSS 渲染任务是最合适的了。和 Web Worker 一样,Worklets 拥有一个隔离于主进程的全局空间,在这个空间里,没有 window 对象,却有 registerPaint、registerLayout 这些全局 API。因此,我们需要这样引入自定义 JS 代码:

if ("paintWorklet" in CSS) {CSS.paintWorklet.addModule("paintworklet.js");
}
if ("layoutWorklet" in CSS) {CSS.layoutWorklet.addModule("layoutworklet.js");
}

基础:三步用上 Painting API

我们来自定义 background-image 属性,它将用于给作用节点绘制一个矩形背景,背景色值由该节点上的一个 CSS 变量 --rect-color 指定。

1、编写一个 Paint 类:

新建一个 paintworklet.js,利用 registerPaint 方法注册一个 Paint 类 rect,定义属性的绘制逻辑:

registerPaint("rect", class {static get inputProperties() {return ["--rect-color"];}paint(ctx, geom, properties) {const color = properties.get("--rect-color")[0];ctx.fillStyle = color;ctx.fillRect(0, 0, geom.width, geom.height);}
});
web前端开发学习Q-q-u-n:784783012 ,分享学习的方法和需要注意的小细节,不停更新最新的教程和学习方法
(学习工具,详细的前端项目实战教程,PDF

上边定义了一个名为 rect 的 Paint 类,当 rect 被使用时,会实例化 rect 并自动触发 paint 方法执行渲染。paint 方法中,我们获取节点 CSS 定义的 --rect-color 变量,并将元素的背景填充为指定颜色。由于需要使用属性 --rect-color,我们需要在静态方法 inputProperties 中声明。

2、Worklets 加载 Paint 类:

HTML 中通过 Worklets 载入上一步骤实现的 paintworklet.js 并注册 Paint 类:

<div class="rect"></div>
<script>if ("paintWorklet" in CSS) {CSS.paintWorklet.addModule("paintworklet.js");}
</script>

3、使用 Paint 类:

CSS 中使用的时候,只需要调用 paint 方法:

.rect {width: 100vw;height: 100vh;background-image: paint(rect);--rect-color: rgb(255, 64, 129);
}

可以看得出利用 CSS Houdini,我们可以像操作 canvas 一样灵活自如地实现我们想要的样式功能。

进阶:实现动态波纹

根据上述步骤,我们演示一下如何用 CSS Painting API 实现一个动态波浪的效果:

<!-- index.html -->
<div id="wave"></div><style>#wave {width: 20%;height: 70vh;margin: 10vh auto;background-color: #ff3e81;background-image: paint(wave);}
</style><script>if ("paintWorklet" in CSS) {CSS.paintWorklet.addModule("paintworklet.js");const wave = document.querySelector("#wave");let tick = 0;  requestAnimationFrame(function raf(now) {tick += 1;wave.style.cssText = `--animation-tick: ${tick};`;requestAnimationFrame(raf);});}
</script>
// paintworklet.js
registerPaint('wave', class {static get inputProperties() {return ['--animation-tick'];}paint(ctx, geom, properties) {let tick = Number(properties.get('--animation-tick'));const {width,height} = geom;const initY = height * 0.4;tick = tick * 2;ctx.beginPath();ctx.moveTo(0, initY + Math.sin(tick / 20) * 10);for (let i = 1; i <= width; i++) {ctx.lineTo(i, initY + Math.sin((i + tick) / 20) * 10);}ctx.lineTo(width, height);ctx.lineTo(0, height);ctx.lineTo(0, initY + Math.sin(tick / 20) * 10);ctx.closePath();ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';ctx.fill();}
})
web前端开发学习Q-q-u-n:784783012 ,分享学习的方法和需要注意的小细节,不停更新最新的教程和学习方法
(学习工具,详细的前端项目实战教程,PDF

paintworklet 中,利用 sin 函数绘制波浪线,由于 AnimationWorklets 尚处于实验阶段,开放较少,这里我们在 worklet 外部用 requestAnimationFrame API 来做动画驱动,让波浪纹动起来。完成后能看到下边这样的效果。

然而事实上这个效果略显僵硬,sin 函数太过于规则了,现实中的波浪应该是不规则波动的,这种不规则主要体现在两个方面:

1)波纹高度(Y)随位置(X)变化而不规则变化

把图按照 x-y 正交分解之后,我们希望的不规则,可以认为是固定某一时刻,随着 x 轴变化,波纹高度 y 呈现不规则变化;

2)固定某点(X 固定),波纹高度(Y)随时间推进而不规则变化

动态过程需要考虑时间维度,我们希望的不规则,还需要体现在时间的影响中,比如风吹过的前一秒和后一秒,同一个位置的波浪高度肯定是不规则变化的。

提到不规则,有朋友可能想到了用 Math.random 方法,然而这里的不规则并不适合用随机数来实现,因为前后两次取的随机数是不连续的,而前后两个点的波浪是连续的。这个不难理解,你见过长成锯齿状的波浪吗?又或者你见过上一刻 10 米高、下一刻就掉到 2 米的波浪吗?

为了实现这种连续不规则的特征,我们弃用 sin 函数,引入了一个包 simplex-noise。由于影响波高的有两个维度,位置 X 和时间 T,这里需要用到 noise2D 方法,它提前在一个三维的空间中,构建了一个连续的不规则曲面:

// paintworklet.js
import SimplexNoise from 'simplex-noise';
const sim = new SimplexNoise(() => 1);registerPaint('wave', class {static get inputProperties() {return ['--animation-tick'];}paint(ctx, geom, properties) {const tick = Number(properties.get('--animation-tick'));this.drawWave(ctx, geom, 'rgba(255, 255, 255, 0.4)', 0.004, tick, 15, 0.4);this.drawWave(ctx, geom, 'rgba(255, 255, 255, 0.5)', 0.006, tick, 12, 0.4);}/*** 绘制波纹*/drawWave(ctx, geom, fillColor, ratio, tick, amp, ih) {const {width,height} = geom;const initY = height * ih;const speedT = tick * ratio;ctx.beginPath();for (let x = 0, speedX = 0; x <= width; x++) {speedX += ratio * 1;var y = initY + sim.noise2D(speedX, speedT) * amp;ctx[x === 0 ? 'moveTo' : 'lineTo'](x, y);}ctx.lineTo(width, height);ctx.lineTo(0, height);ctx.lineTo(0, initY + sim.noise2D(0, speedT) * amp);ctx.closePath();ctx.fillStyle = fillColor;ctx.fill();}
})
web前端开发学习Q-q-u-n:784783012 ,分享学习的方法和需要注意的小细节,不停更新最新的教程和学习方法
(学习工具,详细的前端项目实战教程,PDF

修改峰值和偏置项等参数,可以再画多一个不一样的波浪纹,效果如下,完工!

CSS Houdini实现动态波浪纹相关推荐

  1. css3波浪纹路_CSS Houdini实现动态波浪纹效果

    CSS Houdini 号称 CSS 领域最令人振奋的革新.CSS 本身长期欠缺语法特性,可拓展性几乎为零,并且新特性的支持效率太低,兼容性差.而 Houdini 直接将 CSS 的 API 暴露给开 ...

  2. CSS Houdini:用浏览器引擎实现高级CSS效果

    vivo 互联网前端团队-Wei Xing Houdini被称之为Magic of styling and layout on the web,看起来十分神秘,但实际上,Houdini并非什么神秘组织 ...

  3. [css] 你了解CSS Houdini吗?说说它的运用场景有哪些?

    [css] 你了解CSS Houdini吗?说说它的运用场景有哪些? Houdini是一组底层API,它们公开了CSS引擎的各个部分,从而使开发人员能够通过加入浏览器渲染引擎的样式和布局过程来扩展CS ...

  4. houdini_通过卡通了解CSS Houdini的指南

    houdini 易于阅读的指南. (An easy-to-read guide.) Hey there! 嘿! Thanks for joining me on the journey to unde ...

  5. CSS Houdini

    前言 最近看了几篇文章,是关于 CSS Houdini 的.作为一个前端搬砖的还真不知道这玩意,虽然不知道的东西挺多的,但是这玩意有点高大上啊. Houdini 是一组底层 API,它们公开了 CSS ...

  6. 微信小程序wxss---对应css样式(动态修改css样式)

    微信中wxss有些类似于scss,可以引用外联样式.但是不知道怎么继承css样式 下图是微信中动态绑定css样式,实现css样式的动态修改

  7. CSS实现一个动态横向时间轴

    CSS实现一个动态横向时间轴 先看效果 html <template><x-view class="stepFlex"><x-viewv-for=&q ...

  8. html+css实现多种动态相册

    html+css实现多种动态相册 电子相册 全屏背景切换 照片墙 百叶窗 3d照片墙 立方体相册 代码 电子相册 原理:由a标签的锚点进行图片上的切换,左侧大图用verflow: hidden进行隐藏 ...

  9. 利用CSS实现文字动态背景

    利用CSS实现文字动态背景 实现思路 1.我们要实现的是文字动态背景,那首先就应该想到在文字的基础上添加图片背景,然后利用文字范围进行裁剪,裁剪掉文字以外的背景,最后利用这个css3中的关键帧实现让图 ...

最新文章

  1. 将A*算法讲明白的大牛 感谢原作者Frank_chen 基础是迪克斯特拉算法
  2. 掩码计算工具netmask
  3. Control.BeginInvoke 和 Delegate.BeginInvoke 备忘
  4. 警惕!这7件事情千万不要发生你身上-来自15年程序员的忠告
  5. Linux下批量添加用户的两种方法
  6. Java ThreadLocal
  7. delphi语言转为汇编语言_每天5分钟,轻松建立技术图谱 编程语言黑历史
  8. select a method for export 选项
  9. 出现ESXi系统无法连接FreeNAS的情况?90%以上的人都做错了!
  10. 【VC皮肤】SkinSharp 1.0.6.6的使用
  11. AI2(App Inventor 2)离线版服务器(2019.8.25更新)
  12. 竞争情报(CI,competitive intelligence)都需要哪些因素
  13. 毕业论文排版,格式总结
  14. coreldraw梯形校正_CorelDRAW绘制等腰梯形的多种方法
  15. 论文查重一般包括哪些部分呢?
  16. Android:This usually happens when two views of different type have the same id in the same hierarchy
  17. PUCCH(2)格式与DTX检测(源于5G上行控制信道增强技术研究)
  18. 大数据之分布式协调神器:Zookeeper选举
  19. php程序员自荐信,自荐信范文2017年
  20. rigol 普源MSO5104数字示波器技术参数

热门文章

  1. linux 下ffmpeg和mencoder安装
  2. 初学C语言-二维数组与字符数组
  3. arcgis生成公里格网_ArcGIS出图:地理坐标格网的设定(二)
  4. 【毕业设计_课程设计】基于各种机器学习和深度学习的中文微博情感分析
  5. Hadoop快速入门——第四章、zookeeper安装
  6. AB通过信箱答辩,每个人都可以从自己信箱中取得对方的问题,以及南北桥问题。
  7. Shiro----散列算法(算法加密)
  8. 来内蒙古之星选购腾势X——践行自己的生活态度
  9. Autodesk.ArtCAM.Premium.2017.Suite.build110.Multilang.Win64 1DVD
  10. 权限提升:令牌窃取 || 进程注入.