本文首发于掘金,未经许可严禁转载
一、一个简略案例理解 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 格调按钮的次要思路!
欢送下载源码进行体验,点击跳转。
好了,本文内容就这样,感激浏览,欢送分享。