CSS Houdini实现动态波浪纹效果


Posted in HTML / CSS onJuly 30, 2019

CSS Houdini 号称 CSS 领域最令人振奋的革新。CSS 本身长期欠缺语法特性,可拓展性几乎为零,并且新特性的支持效率太低,兼容性差。而 Houdini 直接将 CSS 的 API 暴露给开发者,以往完全黑盒的浏览器解析流开始对外开放,开发者可以自定义属于自己的 CSS 属性。

背景

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

什么是 CSS Houdini?

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

CSS Houdini实现动态波浪纹效果

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

CSS Properties and Values API

允许在 CSS 中定义变量和使用变量,是目前兼容性最好的一个 API;

Layout API

允许开发者编写自己的 Layout Module,自定义诸如 display 这类的布局属性;

Painting API

允许开发者编写自己的 Paint Module,自定义诸如 background-image 这类的绘制属性。

基础:三步用上 Painting API

1、HTML 中通过 Worklets 载入样式的自定义代码:

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

Worklets 也是 Houdini 提供的 API 之一,负责加载和执行样式的自定义 JS 代码。它类似于 Web Worker,是一个运行于主代码之外的独立工作进程,但比 Worker 更为轻量,负责 CSS 渲染任务最为合适。

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

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);
    }
  }
);

上边定义了一个名为 rect 的 paint 属性类,当 rect 被使用时,会实例化 rect 并自动触发 paint 方法执行渲染。paint 方法中,我们获取节点 CSS 定义的 --rect-color 变量,并将元素的背景填充为指定颜色。ctx 参数是一个 Canvas 的 Context 对象,因此 paint 的逻辑跟 Canvas 的绘制方式一样。

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

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

这是一个自定义 CSS 背景色属性的简单实现,看得出利用 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();
  }
})

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

CSS Houdini实现动态波浪纹效果
 

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

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

CSS Houdini实现动态波浪纹效果
 

把图按照 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();
  }
})

 

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

CSS Houdini实现动态波浪纹效果

总结

以上所述是小编给大家介绍的CSS Houdini实现动态波浪纹效果,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

HTML / CSS 相关文章推荐
css3 border-image使用说明
Jun 23 HTML / CSS
推荐10个CSS3 制作的创意下拉菜单效果
Feb 11 HTML / CSS
实例讲解CSS3中Transform的perspective属性的用法
Apr 22 HTML / CSS
5分钟让你掌握css3阴影、倒影、渐变小技巧(小编推荐)
Aug 15 HTML / CSS
有关HTML5 Video对象的ontimeupdate事件(Chrome上无效)的问题
Jul 19 HTML / CSS
让IE9以下版本的浏览器兼容HTML5的方法
Mar 12 HTML / CSS
HTML5 文件上传下载的实例代码
Jul 03 HTML / CSS
HTML5 Canvas 旋转风车绘制
Aug 18 HTML / CSS
导出HTML5 Canvas图片并上传服务器功能
Aug 16 HTML / CSS
高清屏下canvas重置尺寸引发的问题的解决
Oct 14 HTML / CSS
HTML基础-标签分类(闭合标签,空标签,块级元素,行内元素,行级块元素,可替换元素)
Mar 31 HTML / CSS
el-form每行显示两列底部按钮居中效果的实现
Aug 05 HTML / CSS
使用css实现android系统的loading加载动画
Jul 25 #HTML / CSS
使用CSS3实现input多选框自定义样式的方法示例
Jul 19 #HTML / CSS
详解css3中 text-fill-color属性
Jul 08 #HTML / CSS
解决margin 外边距合并问题
Jul 03 #HTML / CSS
css3实现蒙版弹幕功能
Jun 18 #HTML / CSS
CSS3中媒体查询结合rem布局适配手机屏幕
Jun 10 #HTML / CSS
纯CSS3制作页面切换效果的实例代码
May 30 #HTML / CSS
You might like
两种php调用Java对象的方法
2006/10/09 PHP
4.与数据库的连接
2006/10/09 PHP
php数组函数序列之array_intersect() 返回两个或多个数组的交集数组
2011/11/10 PHP
php实现的zip文件内容比较类
2014/09/24 PHP
php好代码风格的阶段性总结
2016/06/25 PHP
修改Laravel5.3中的路由文件与路径
2016/08/10 PHP
PHP读取文件的常见几种方法
2016/11/03 PHP
dojo 之基础篇(三)之向服务器发送数据
2007/03/24 Javascript
关于jquery append() html时的小问题的解决方法
2010/12/16 Javascript
javascript中数组的冒泡排序使用示例
2013/12/18 Javascript
ZeroClipboard插件实现多浏览器复制功能(支持firefox、chrome、ie6)
2014/08/30 Javascript
JQuery用户名校验的具体实现
2016/03/18 Javascript
JS使用正则截取两个字符串之间的字符串实现方法详解
2017/01/06 Javascript
vue2.0 axios前后端数据处理实例代码
2017/06/30 Javascript
使用JavaScript进行表单校验功能
2017/08/01 Javascript
Bootstrap 模态框多次显示后台提交多次BUG的解决方法
2017/12/26 Javascript
微信小程序中添加客服按钮contact-button功能
2018/04/27 Javascript
angular4自定义表单控件[(ngModel)]的实现
2018/11/23 Javascript
[07:26]2015国际邀请赛第二日TOP10集锦
2015/08/06 DOTA
[52:41]OG vs IG 2018国际邀请赛小组赛BO2 第二场 8.18
2018/08/20 DOTA
用python实现批量重命名文件的代码
2012/05/25 Python
Python判断变量是否已经定义的方法
2014/08/18 Python
Python第三方库的安装方法总结
2016/06/06 Python
Python编程修改MP3文件名称的方法
2017/04/19 Python
Python计算斗牛游戏概率算法实例分析
2017/09/26 Python
python如何定义带参数的装饰器
2018/03/20 Python
python编辑用户登入界面的实现代码
2018/07/16 Python
用Python调用win命令行提高工作效率的实例
2019/08/14 Python
详解Python list和numpy array的存储和读取方法
2019/11/06 Python
浅谈spring boot 集成 log4j 解决与logback冲突的问题
2020/02/20 Python
记一次Django响应超慢的解决过程
2020/09/17 Python
JD Sports比利时官网:英国领先的运动鞋和运动服饰零售商
2018/10/10 全球购物
商场消防演习方案
2014/02/12 职场文书
2015年党风廉政承诺书
2015/01/22 职场文书
房地产公司工程部经理岗位职责
2015/04/09 职场文书
2016年“我们的节日·中秋节”活动总结
2016/04/05 职场文书