CSS Paint API

2018-01-31 11:00:37来源:https://www.w3cplus.com/css/css-paint-api.html作者:W3CPlus人点击

分享

CSS Paint API是W3C规范中之一, 目前的版本是Level1 。它也被称为 CSS Custom Paint 或者 Houdini's Paint Worklet 。对于开发者而言,有一个值得高兴的是,Chrome65将会支持该API。也就是说,可以使用CSS Paint API提供的 registerPaint(name, paintCtor) 做一些事情。


那么CSS Paint API是什么?你能用它做什么?它又是如何工作的呢?带着一系列的为什么,我们开启对CSS Paint API的初探。


什么是CSS Paint

先来了解CSS Paint是什么?在理解这个概念之前,我们先来回忆一下,我们在平时写CSS时是如何给一个元素设置背景图片。了解CSS的同学,都应该知道,采用 background-image 属性,比如:


background-image: url(xxx.jpg)

或者:


background-image: linear-gradient(to bottom, red, green)

除了给 background-image 指定一个图片之外,还可以是渐变(CSS中的渐变相当于一张背景图片)。而我们要了解的CSS Paint则是通过JavaScript的方式,让你在CSS中能够引入用JavaScript编写的图形。感觉有点类似于HTML5中的 canvas ,对吗?如果你继续往后看,你会越加有这样的感觉。


写一个Paint Worklet

先定义一个叫作 myPainter 的Paint Worklet,接下来使用 CSS.paintWorklet.addModule('my-paint-worklet.js') 来加载已定义好的CSS Paint Worklet。在 my-paint-worklet.js 文件中,使用 registerPaint 函数来注册一个Paint Woklet的类:


class MyPainter {
paint(ctx, geometry, properties) {
// ...
}
}
registerPaint('myPainter', MyPainter)

在 paint() 回调中,我们可以使用 <canvas> 中 CanvasRenderingContext2D 的 ctx 方法。如果你熟悉 <canvas> ,那么你就可以知道怎么绘制Paint Worklet。 geometry 用来指定画布的 width 和 height , properties 可以获取自定义元素属性,这么说有点抽象,后面会介绍到该属性。


特别声明:CSS Paint中的Paint Worklet的 ctx 和 <canvas> 中的 ctx 并不是百分之百的相同。到目前为止,文本渲染的方法是无法使用的,这主要是出于安全原因,你无法从画布上读取像素。


来看一个简单的示例。先创建一个 index.html 文件:


<!doctype html>
<html>
<head>
<style>
body {
width: 100vw;
height: 100vh;
background-image: paint(checkerboard);
}
</style>
<script>
CSS.paintWorklet.addModule('checkerboard.js')
</script>
</head>
<body>
</body>
</html>

然后创建 checkerboard.js ,并在这个文件中添加下面的代码:


class CheckerboardPainter {
paint(ctx, geom, properties) {
const colors = ['red', 'green', 'blue'];
const size = 32;
for(let y = 0; y < geom.height/size; y++) {
for(let x = 0; x < geom.width/size; x++) {
const color = colors[(x + y) % colors.length];
ctx.beginPath();
ctx.fillStyle = color;
ctx.rect(x * size, y * size, size, size);
ctx.fill();
}
}
}
}
registerPaint('checkerboard', CheckerboardPainter);

到时将会看到的效果如下所示:



上面的示例,先定义了一个叫 CheckboardPainter 的Paint Worklet,并且将之注册为 checkboard ,然后通过 CSS.paintWorklet.addModule() 的方法加载这个Paint Worklet。最后在CSS中,使用 paint(checkboard) 给指定的元素添加背景图。其最终效果正如上图所示。


可能你会纳闷,这样的效果使用 background-image 就能实现(不管是调用图片,还是使用 linear-gradient 都可以实现类似效果)。那他们两者之间有何区别吗?其实两者是有所区别的:


CSS Paint与 background-image 的差别就是 background-image 是根据代码计算出来的,不会随着元素的大小变化而伸缩。而CSS Paint绘制的图像总是会和元素容器所需保持一样的大。也就是说,让你修改元素大小可视区域时,CSS Paint绘制的图像会重新绘制。言外之意,背景图像总是和它所需要的一样大,包括对高密度(Hight-density)显示器的补偿。


是不是很酷。


个性化Paint Worklet

前面提到过,定义一个Paint Worklet时 paint() 方法接受三个参数,其中第三个参数 properties 可以让Paint Worklet可以访问其他CSS属性,这就是 properties 参数的强大之处。通过一个静态的类,比如 inputProperties 属性,你可以对任何CSS属性,包括自定义属性进行更改。这些值将通过 properties 参数提供给你。


比如下面这个例子。同样先创建一个 index.html 文件,在文件中加入下面的代码:


<!doctype html>
<html>
<head>
<style>
body {
width: 100vw;
height: 100vh;
--checkerboard-spacing: 10;
--checkerboard-size: 32;
background-image: paint(checkerboard);
}
</style>
<script>
CSS.paintWorklet.addModule('checkerboard.js')
</script>
</head>
<body>
</body>
</html>

然后在创建的 checkerboard.js 文件中加入下面的代码:


class CheckerboardPainter {
static get inputProperties() {
return [
'--checkerboard-spacing',
'--checkerboard-size'
]
}
paint(ctx, geom, properties) {
const size = parseInt(properties.get('--checkerboard-size').toString());
const spacing = parseInt(properties.get('--checkerboard-spacing').toString());
const colors = ['red', 'green', 'blue'];
for(let y = 0; y < geom.height/size; y++) {
for(let x = 0; x < geom.width/size; x++) {
ctx.fillStyle = colors[(x + y) % colors.length];
ctx.beginPath();
ctx.rect(x*(size + spacing), y*(size + spacing), size, size);
ctx.fill();
}
}
}
}
registerPaint('checkerboard', CheckerboardPainter);

现在我们可以使用相同的代码来处理所有不同类型的格子效果。但更爽的是,现在可以通过开发者调试工具, 在找到正确的外观之前 ,对这些值进行修改。



在 CheckerboardPainter 类中,给静态属性 inputProperties 定义了两个属性: --checkerboard-spacing 和 --checkerboard-size ,这两个属性可以像一般的 CSS 的属性一样使用于 HTML 元素,而这两个属性的值将被 paint() 的第三个参数 properties 获得,被用于生产图像。所以,如果修改 body 的 --checkerboard-spacing 或者 --checkerboard-size 属性,背景图将会发生改变,正如上面的录屏展示的效果一样。


注意:如果把颜色也参数化是不是很有意? spec 允许 paint() 函数获取参数列表。这个特性还没有在Chrome中实现,因为它目前还严重依赖于Houdini的属性和值的API,让其能正常工作,还需要一些时间。


不支持Paint Worklet的浏览器

到目前为止,只有Chrome浏览器支持Paint Worklet(Chrome 65可以看到效果)。尽管其他浏览器都发出响应将会支持CSS Paint API的特性,但到目前依旧没有啥进展。为了跟上时代的发展, Houdini支持吗 ?与此同时,就算浏览器还不支持CSS Paint的Paint Worklet,我们也要确保使用渐进增加来保证你的代码能正常运行。为了确保这一点,你必须在CSS和JavaScript中调整你的代码。


如果你从未接触过Houdini,甚至说都不知道他是什么?建议你点击这里对Houdini进行一些简单的了解。另外这里有一个 CSS Houdini的示例仓库 ,这些示例将向你展示Houdini的神奇之处。


回到正题上来吧,对于不支持的浏览器,可以通过检查CSS对象,实现对JavaScript中Paint Worklet支持情况做一个检测:


if ('paintWorklet' in CSS) {
CSS.paintWorklet.addModule('mystuff.js');
}

而在CSS通过 @supports 来做相应的检测:


@supports (background: paint(id)) {
body {
background-image: paint(checkerboard);
}
}

另外,众所周知,如果浏览器遇到一个未知的属性,则会忽略此属性声明的规则。如果你对同一个属性进行两次声明,前者是CSS的属性,其后紧跟Paint Worklet,你就可以对不支持CSS Paint的浏览器做降级处理。比如下面这样:


body {
background-image: linear-gradient(0, red, blue);
background-image: paint(myGradient, red, blue);
}

这样一来,支持CSS Paint的浏览器,第二个 background-image 将会覆盖第一个。在不支持CSS Paint的浏览器,第二个属性规则将会示为无效,而第一个 background-image 将会起作用。


示例

在众多CSS Paint的示例中,其中一些比其他的示列更易理解。其中一个比较易于理解的示例就是使用CSS Paint来减少对DOM元素。通常,元素的添加纯粹是为了使用CSS创建修饰。例如,在 Material Design Lite 中的 button 的Ripple效果。为了实现这个效果,添加了两个额外的 <span> 元素。如果你的Web页面有很多这样的按钮效果,那么你就要增加很多个DOM元素,因此可能影响你的页面性能。如果使用 CSS Paint来实现Ripple效果 ,你不需要添加任何额外的DOM元素。此外,你还拥有更易于自定义的属性。


使用CSS Paint的另一个好处是,在大多数的情况下,能解决很多CSS无法解决的事情,而且代码量也少。当然,其中也有一个取舍问题。当画布的大小或任何参数发生变化时,绘图的代码将会运行。因此,如果你的代码很复杂,那需要很长的时间,它可能会引入jank。Chrome正在处理主要线程上的Paint Worklet,因此即使运行时间长,Paint Worklet也不会影响主线程的响应能力。


对于我来说,最令人兴奋的前景是,CSS Paint可以很快的为CSS新特性创建Polyfill。比如conic-gradient 的Polyfill。另外的一个例子,在CSS会议中,它决定你现在可以有多个边框颜色。在这个会议还在进行之时,@Kilpatrick就通过CSS Paint为其 写了一个对应的Polyfill 。


不规则盒子的思考

大多数人在学习CSS Paint时都从 background-image 和 border-image 着手。其实另一个值得我们思考的是,如果使用 CSS Paint的 mask-image 可以让DOM元素具有任意形状。比如 钻石形状 :



写在最后

CSS Paint在Chrome Canary中已有一段时间。值得庆幸的是,在Chrome 65中将会默认启用CSS Paint。对于开发者而言,我们应该不断去尝试新的可能性,CSS Paint将会让我们打开更多的思路,为你的灵感提供更多于创作的空间。可以看看 @Vincent De Oliveira收藏的一些案例 。或许在这些案例中,你也能产生一些新的灵感。


特别声明:此篇文章内容来源于 @Surma 的《 CSS Paint API 》一文。




大漠

常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等前端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《 图解CSS3:核心技术与案例实战 》。



如需转载,烦请注明出处: https://www.w3cplus.com/css/css-paint-api.html


最新文章

123

最新摄影

闪念基因

微信扫一扫

第七城市微信公众平台