本文首发于掘金,未经许可严禁转载
一、一个简略案例理解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 大略的应用形式,先总结下:
- CSS 中应用 paint 办法
- JS 增加绘制代码脚本
- ripple.js 中像写 Canvas 一样绘制图形
目前为止,得倒如下动态按钮:
二、如何实现动态效果
先来思考如何实现涟漪波纹状成果。
先来思考动画如何实现,置信大家都看过动画片,其理论就是多张动态图连贯在一起组成。
当动态图越多,其动画成果越晦涩。
那么咱们将水波纹动态效果能够拆解一下:
- 以某点为圆形画圆(带 background 的圆)
- 圆的半径逐步变大,直至隐没出按钮边界
简略画下,横轴为工夫线,随着工夫圆缓缓变大。
那么如何连贯成动画呢,下面说动态图片越多越好,那在计算机上这样一个动画要多少张动态图最为适合呢?有人会说了,那间接应用定时器SetInterval
,一直的画圆,同时直径缓缓变大。当半径大到肯定水平的时候return
执行不就行了嘛?
这是一种思路,但 SetInterval
是 macro-task(宏工作),和 SetTimeout
一样,cb 执行工夫会受到线程其它工作的影响,动画成果并不现实。
一说到定时器,可能有人想到 requestAnimationFrame
了,没错,比起 setTimeout、setInterval 它有两点劣势:
- requestAnimationFrame 会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就实现,并且重绘或回流的工夫距离紧紧追随浏览器的刷新频率,一般来说,这个频率为每秒60帧。
- 在暗藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的的cpu,gpu和内存使用量。
上代码:
document.querySelector('button').addEventListener('click', evt => { requestAnimationFrame(function raf(now) { // 1. 一直刷新 // 2. 一直画圆 // 3. 满足某条件,return 进来!进行画圆 requestAnimationFrame(raf); }) })
当咱们点击按钮的时候,requestAnimationFrame
会被执行 16.7ms/次,通过一直扭转圆形大小,来实现视觉上的涟漪动画成果。requestAnimationFrame
中咱们做三件事:
- 一直刷新
- 一直画圆
- 满足某条件,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(); }});
总结:
- 【一直刷新】:requestAnimationFrame
- 【一直画圆】:requestAnimationFrame + CSS Paint API
- 【满足某条件,return 进来!进行画圆】:超出1s,画圆动作进行
以上三点就是基于 CSS Houdini 实现一个 material 格调按钮的次要思路!
欢送下载源码进行体验,点击跳转。
好了,本文内容就这样,感激浏览,欢送分享。