关于css:用CSS-Houdini实现一个Material风格的按钮

12次阅读

共计 2546 个字符,预计需要花费 7 分钟才能阅读完成。

本文首发于掘金,未经许可严禁转载

一、一个简略案例理解 CSS Paint

例如,咱们心愿模仿实现一个谷歌 material 格调的波纹按钮。如下这样:

残缺 CSS 代码及 JS 代码如下:

    .ripple {
      width: 100px;
      height: 50px;
      display: flex;
      justify-content: center;
      align-items: center;
      color: #fff;
      border: none;
      font-size: 16px;
      border-radius: 6px;
      background-color: rgb(64 179 255);

      --ripple-x: 0;
      --ripple-y: 0;
      --animation-tick: 0;
    }
    /* 点击后减少的动画成果 */
    .ripple.animating {background-image: paint(ripple);
    }

HTML 代码如下:

  <button class="ripple"> Click me! </button>
  
  <script>
    CSS.paintWorklet.addModule('ripple.js')
  </script>

绘制图形 JS 须要以模块引入,CSS.paintWorklet.addModule 能让 web 引入 ripple.js 这个脚本,并另外开拓线程执行。它不会影响主线程,这是 worklet 的重要“卖点”!

ripple.js 代码如下:

registerPaint('ripple', class {paint(ctx, geom, properties) {// 像写 canvas 一样绘制动画}
});

以上就是 CSS Paint API 大略的应用形式,先总结下:

  1. CSS 中应用 paint 办法
  2. JS 增加绘制代码脚本
  3. ripple.js 中像写 Canvas 一样绘制图形

目前为止,得倒如下动态按钮:

二、如何实现动态效果

先来思考如何实现涟漪波纹状成果。
先来思考动画如何实现,置信大家都看过动画片,其理论就是多张动态图连贯在一起组成。

当动态图越多,其动画成果越晦涩。

那么咱们将水波纹动态效果能够拆解一下:

  1. 以某点为圆形画圆(带 background 的圆)
  2. 圆的半径逐步变大,直至隐没出按钮边界
    简略画下,横轴为工夫线,随着工夫圆缓缓变大。

    那么如何连贯成动画呢,下面说动态图片越多越好,那在计算机上这样一个动画要多少张动态图最为适合呢?有人会说了,那间接应用定时器 SetInterval,一直的画圆,同时直径缓缓变大。当半径大到肯定水平的时候 return 执行不就行了嘛?

这是一种思路,但 SetInterval 是 macro-task(宏工作),和 SetTimeout 一样,cb 执行工夫会受到线程其它工作的影响,动画成果并不现实。

一说到定时器,可能有人想到 requestAnimationFrame 了,没错,比起 setTimeout、setInterval 它有两点劣势:

  1. requestAnimationFrame 会把每一帧中的所有 DOM 操作集中起来,在一次重绘或回流中就实现,并且重绘或回流的工夫距离紧紧追随浏览器的刷新频率,一般来说,这个频率为每秒 60 帧。
  2. 在暗藏或不可见的元素中,requestAnimationFrame 将不会进行重绘或回流,这当然就意味着更少的的 cpu,gpu 和内存使用量。

上代码:

document.querySelector('button').addEventListener('click', evt => {requestAnimationFrame(function raf(now) {
      // 1. 一直刷新
      // 2. 一直画圆
      // 3. 满足某条件,return 进来!进行画圆
      requestAnimationFrame(raf);
    })
  })

当咱们点击按钮的时候,requestAnimationFrame 会被执行 16.7ms/ 次,通过一直扭转圆形大小,来实现视觉上的涟漪动画成果。requestAnimationFrame 中咱们做三件事:

  1. 一直刷新
  2. 一直画圆
  3. 满足某条件,return 进来!进行画圆

那么如何一直刷新咱们解决了,再来思考如何一直画圆?

这里咱们能够在点击的时候为按钮增加一个动画的 class,为按钮增加一个 background-image!如果你看过上一篇文章,那一看到 background-image 应该会立马想到 Houdini 中的 CSS Paint API。它能够动静扭转 CSS 变量,那么间接上代码:

registerPaint('ripple', class {static get inputProperties() {return ['--animation-tick', '--ripple-x', '--ripple-y']; 
  }

  // Canvas 画圆
  paint(ctx, geom, properties) {
    // 点击事件的坐标,作为画圆的圆形
    const x = parseFloat(properties.get('--ripple-x').toString());
    const y = parseFloat(properties.get('--ripple-y').toString());
    // 以后倒计时剩余时间
    let tick = parseFloat(properties.get('--animation-tick').toString());

    // 倒计时在 1 秒内,超出,Canvas 画圆动作完结
    if(tick < 0) tick = 0;
    if(tick > 1000) tick = 1000;

    ctx.fillStyle = '#ddd'; // 圆形背景色彩
    ctx.globalAlpha = 0.5; // 背景透明度

    // 画圆
    ctx.arc(
      x, y, // 圆心坐标
      geom.width * tick/1000, // 半径
      0, // 起始角
      2 * Math.PI, // 完结角
    );

    ctx.fill();}
});

总结:

  1. 【一直刷新】:requestAnimationFrame
  2. 【一直画圆】:requestAnimationFrame + CSS Paint API
  3. 【满足某条件,return 进来!进行画圆】:超出 1s,画圆动作进行

以上三点就是基于 CSS Houdini 实现一个 material 格调按钮的次要思路!

欢送下载源码进行体验,点击跳转。

好了,本文内容就这样,感激浏览,欢送分享。

正文完
 0