Open jyzwf opened 5 years ago
关于Houdini的印象还是在去年,然后也只是听过有这么个东西,但也只仅仅停留在听过。但为何今天会重新留意这个东西?因为闲来无聊,就去逛了某大佬的博客,逛着逛着,突然看到他写的一篇关于水波纹,重点不在于此,在于blog的最后来了句,用Houdini来实现,动画会更加容易。于是就打算看看这个东西。 关于水波纹的实现,到codeopen上一搜一大堆,直接来一个链接吧,Ripple Button。如果单用css来实现,可以考虑使用伪类加径向渐变,最后加点transition来实现,如果加上js来获取点击时的位置,就能做到更加好看的效果,这个不是文本的重点:
Houdini
伪类加径向渐变
transition
js
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>水波纹</title> <style> button { display: inline-block; text-align: center; white-space: nowrap; cursor: pointer; border: none; padding: 8px 18px; margin: 10px 1px; font-size: 14px; font-weight: 500; text-transform: uppercase; background: transparent; background-color: #00bcd4; color: rgba(0, 0, 0, 0.87); } .ripple { position: relative; overflow: hidden; } .ripple:after { content: ''; display: block; position: absolute; width: 100%; height: 100%; top: 0; left: 0; pointer-events: none; background-image: radial-gradient( circle, #666 10%, transparent 10.01% ); background-repeat: no-repeat; background-position: 50%; transform: scale(10, 10); opacity: 0; transition: transform 0.3s, opacity 0.5s; } .ripple:active:after { transform: scale(0, 0); opacity: 0.3; transition: 0s; } </style> </head> <body> <button class="button ripple">按钮</button> </body> </html>
总的来说Houdini就是开发者可以调用CSS的API,来扩展CSS,并且允许开发者参与到浏览器渲染引擎的样式和布局流程中。它提供了诸如painting API,Layout API,parsing API,Typed OM等API。接下来我们就来试试其中的paint api(由于有些浏览器暂不支持),主要参考官方提供的demo。
painting API
Layout API
parsing API
Typed OM
paint api
Paint API 必须要在支持 https 服务器上或者本地 localhost 上才能使用。所以如果你是在本地开发,可以用 http-server 在本地快速搭建一个服务器。 要记得禁用浏览器缓存,让最新的 worklets 立马生效。 目前暂时无法在 worklets 中打断点或者插入 debugger ,不过 console.log() 还是可以用的。 这里我尝试了使用parcel来本地开发,但必须将worklet放置在dist目录下才能生效:
Paint API
localhost
http-server
禁用
缓存
worklets
debugger
console.log()
parcel
worklet
dist
painting
同样我们实现其上面水波纹效果
<!DOCTYPE html> <html lang="en"> <head> <title>Document</title> <style> #ripple { width: 300px; height: 300px; border-radius: 150px; font-size: 5em; line-height: 300px; background-color: rgb(255, 64, 129); border: 0; box-shadow: 0 1px 1.5px 0 rgba(0, 0, 0, 0.12), 0 1px 1px 0 rgba(0, 0, 0, 0.24); color: white; --ripple-x: 0; --ripple-y: 0; // 使用css 变量,在后面的worklet中可以被获取到 --ripple-color: rgba(255, 255, 255, 0.54); --animation-tick: 0; } #ripple:focus { outline: none; } #ripple.animating { background-image: paint(ripple); // 这里就是painting真正使用的地方,其中 ripple 是在worklet中定义的属性 } </style> </head> <body> <div id="ripple">Houdini</div> <script src="./index.js"></script> </body> </html>
下面是相关的动画实现,其中借用了 requestAnimationFrame:
requestAnimationFrame
if (location.protocol === 'http:' && location.hostname !== 'localhost') location.protocol = 'https:'; if ('paintWorklet' in CSS) { CSS.paintWorklet.addModule('./app.js'); } else { document.body.innerHTML = 'You need support for <a href="https://drafts.css-houdini.org/css-paint-api/">CSS Paint API</a> to view this demo :('; } const button = document.querySelector('#ripple'); let start = performance.now(); let x, y; document.querySelector('#ripple').addEventListener('click', evt => { button.classList.add('animating'); [x, y] = [evt.clientX, evt.clientY]; start = performance.now(); requestAnimationFrame(function raf(now) { console.log(start, now); const count = Math.floor(now - start); // 这里借用了css 变量,然后就可以在worklet中动态获取值 button.style.cssText = `--ripple-x: ${x}; --ripple-y: ${y}; --animation-tick: ${count};`; if (count > 1000) { button.classList.remove('animating'); button.style.cssText = `--animation-tick: 0`; return; } requestAnimationFrame(raf); }); });
重点来了,我们定义的paint api放在哪里呢?它不能直接像js一样被嵌入调用,而是需要使用worklet来帮助我们,像上面看的一样CSS.paintWorklet.addModule('./app.js');,在paintWorklet中添加一个我们自定义的脚本模块。worklet独立于主线程之外,不直接与DOM互动,特点为轻量且生命周期较短。 worklet生命周期:
CSS.paintWorklet.addModule('./app.js');
paintWorklet
主线程
worklet进程
独立于
阻塞
worklet.addModule
下面是我们注册的 ripple:
ripple
registerPaint( 'ripple', class { static get inputProperties() { return [ '--ripple-color', '--animation-tick', 'background-color', '--ripple-x', '--ripple-y', ]; } paint(ctx, geom, properties) { console.log(ctx, geom, properties); const bgColor = properties.get('background-color').toString(); const rippleColor = properties.get('--ripple-color').toString(); const x = parseFloat(properties.get('--ripple-x').toString()); const y = parseFloat(properties.get('--ripple-y').toString()); let tick = parseFloat( properties.get('--animation-tick').toString() ); if (tick < 0) tick = 0; if (tick > 1000) tick = 1000; ctx.fillStyle = bgColor; ctx.fillRect(0, 0, geom.width, geom.height); ctx.fillStyle = rippleColor; ctx.globalAlpha = 1 - tick / 1000; ctx.arc(x, y, (geom.width * tick) / 1000, 0, 2 * Math.PI); ctx.fill(); } } );
这里调用了registerPaint来注册我们需要的ripple,该函数由一个name和paint类构成,其中inputProperties 静态方法用来获取该元素上的一些css属性值,并且传入到paint方法中作为第三个参数。paint方法就是我们实际"绘画"的地方,它提供了三个参数:一个canvas的context变量、当前画布的大小即当前dom元素的大小,以及当前dom元素的css属性properties(在inputProperties返回的)。其中canvas的context变量没有实现canvas中例如对img的处理、drawFocusIfNeeded/scrollPathIntoView、fillText/strokeText、CanvasTextAlign/CanvasTextBaseline等的实现。总的来说就是利用canvas来进行绘图,这里就有想象力了。
registerPaint
name
paint类
inputProperties
css
paint
canvas的context变量
大小
dom元素的css属性
layout api与paint的api编写方式基本一样,不过调用的是registerLayout,以及类里面的调用方法也有所不同,但浏览器的支持性不是那么好,试了一个官方提供的demo都没生效,-_-,还有 animation-worklet,直接告诉我不支持,用其polyfill也还是有bug。houdini还是有一段路要走啊......
layout
registerLayout
-_-
animation-worklet
polyfill
bug
houdini
关于各个浏览器对Houdini的推进程度,看你点击如下链接查看:Is Houdini ready yet‽
至于其他的几个api以及houdini的现状,有兴趣的可以从下面的参考资料中了解:
Houdini:CSS 领域最令人振奋的革新 和 Houdini, CSS Paint API 打个招呼吧 Houdini Samples:houdini的demo CSS-TAG Houdini Editor Drafts:houdini编辑草案 Houdini工作组规范
关于
Houdini
的印象还是在去年,然后也只是听过有这么个东西,但也只仅仅停留在听过。但为何今天会重新留意这个东西?因为闲来无聊,就去逛了某大佬的博客,逛着逛着,突然看到他写的一篇关于水波纹,重点不在于此,在于blog的最后来了句,用Houdini
来实现,动画会更加容易。于是就打算看看这个东西。 关于水波纹的实现,到codeopen上一搜一大堆,直接来一个链接吧,Ripple Button。如果单用css来实现,可以考虑使用伪类加径向渐变
,最后加点transition
来实现,如果加上js
来获取点击时的位置,就能做到更加好看的效果,这个不是文本的重点:Houdini简介
总的来说Houdini就是开发者可以调用CSS的API,来扩展CSS,并且允许开发者参与到浏览器渲染引擎的样式和布局流程中。它提供了诸如
painting API
,Layout API
,parsing API
,Typed OM
等API。接下来我们就来试试其中的paint api
(由于有些浏览器暂不支持),主要参考官方提供的demo。准备
Paint API
必须要在支持 https 服务器上或者本地localhost
上才能使用。所以如果你是在本地开发,可以用http-server
在本地快速搭建一个服务器。 要记得禁用
浏览器缓存
,让最新的worklets
立马生效。 目前暂时无法在worklets
中打断点或者插入debugger
,不过console.log()
还是可以用的。 这里我尝试了使用parcel
来本地开发,但必须将worklet
放置在dist
目录下才能生效:试试
painting
同样我们实现其上面水波纹效果
下面是相关的动画实现,其中借用了
requestAnimationFrame
:重点来了,我们定义的
paint api
放在哪里呢?它不能直接像js一样被嵌入调用,而是需要使用worklet来帮助我们,像上面看的一样CSS.paintWorklet.addModule('./app.js');
,在paintWorklet
中添加一个我们自定义的脚本模块。worklet独立于主线程之外,不直接与DOM互动,特点为轻量且生命周期较短。 worklet生命周期:主线程
worklet进程
,用于"存放"后续被加载进来的worklet
。这些进程独立于
主线程,这样就不会阻塞
主线程worklet.addModule
来异步加载worklet
下面是我们注册的
ripple
:这里调用了
registerPaint
来注册我们需要的ripple
,该函数由一个name
和paint类
构成,其中inputProperties
静态方法用来获取该元素上的一些css
属性值,并且传入到paint
方法中作为第三个参数。paint方法就是我们实际"绘画"的地方,它提供了三个参数:一个canvas的context变量
、当前画布的大小
即当前dom元素的大小,以及当前dom元素的css属性
properties(在inputProperties返回的)。其中canvas的context变量没有实现canvas中例如对img的处理、drawFocusIfNeeded/scrollPathIntoView、fillText/strokeText、CanvasTextAlign/CanvasTextBaseline等的实现。总的来说就是利用canvas来进行绘图,这里就有想象力了。layout
api与paint
的api编写方式基本一样,不过调用的是registerLayout
,以及类里面的调用方法也有所不同,但浏览器的支持性不是那么好,试了一个官方提供的demo都没生效,-_-
,还有animation-worklet
,直接告诉我不支持,用其polyfill
也还是有bug
。houdini
还是有一段路要走啊......关于各个浏览器对Houdini的推进程度,看你点击如下链接查看:Is Houdini ready yet‽
至于其他的几个api以及houdini的现状,有兴趣的可以从下面的参考资料中了解:
参考资料:
Houdini:CSS 领域最令人振奋的革新 和 Houdini, CSS Paint API 打个招呼吧 Houdini Samples:houdini的demo CSS-TAG Houdini Editor Drafts:houdini编辑草案 Houdini工作组规范