在着色器、网格和滤镜的世界中,Canvas2D 可能不会让您感到兴奋。但应该会!
30%–40% 的网页具有 <canvas>
元素,98% 的画布使用 Canvas2D 渲染上下文。Canvas2D 可用于汽车、冰箱,甚至太空(真的)。
诚然,在最先进的 2D 绘制方面,该 API 略微落后。幸运的是,我们一直在努力在 Canvas2D 中实现新功能,以赶上 CSS 的步伐、简化人体工学设计并提升性能。
第 1 部分:了解 CSS
CSS 有一些绘制命令,Canvas2D 中却缺少。在新 API 中,我们添加了一些呼声最高的功能:
圆角矩形
圆角矩形:互联网、计算技术和文明的基石。
从本质上讲,圆角矩形非常有用:您可以将其作为按钮、聊天气泡、缩略图、对话气泡等您命名。在 Canvas2D 中一直可以创建圆角矩形,只是过程有点繁琐:
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'magenta';
const top = 10;
const left = 10;
const width = 200;
const height = 100;
const radius = 20;
ctx.beginPath();
ctx.moveTo(left + radius, top);
ctx.lineTo(left + width - radius, top);
ctx.arcTo(left + width, top, left + width, top + radius, radius);
ctx.lineTo(left + width, top + height - radius);
ctx.arcTo(left + width, top + height, left + width - radius, top + height, radius);
ctx.lineTo(left + radius, top + height);
ctx.arcTo(left, top + height, left, top + height - radius, radius);
ctx.lineTo(left, top + radius);
ctx.arcTo(left, top, left + radius, top, radius);
ctx.stroke();
为了实现简单的圆角矩形,所有这些都是必不可少的:
使用新 API 时,可以使用 roundRect()
方法。
ctx.roundRect(upper, left, width, height, borderRadius);
因此,上述代码可以完全替换为:
ctx.roundRect(10, 10, 200, 100, 20);
ctx.roundRect()
方法还会接受一个 borderRadius
参数数组,该数组最多包含四个数字。这些半径的控制方式与 CSS 相同,可控制圆角矩形的四个角。例如:
ctx.roundRect(10, 10, 200, 100, [15, 50, 30]);
圆锥形渐变
您已经了解了线性渐变:
const gradient = ctx.createLinearGradient(0, 0, 200, 100);
gradient.addColorStop(0, 'blue');
gradient.addColorStop(0.5, 'magenta');
gradient.addColorStop(1, 'white');
ctx.fillStyle = gradient;
ctx.fillRect(10, 10, 200, 100);
径向渐变:
const radialGradient = ctx.createRadialGradient(150, 75, 10, 150, 75, 70);
radialGradient.addColorStop(0, 'white');
radialGradient.addColorStop(0.5, 'magenta');
radialGradient.addColorStop(1, 'lightblue');
ctx.fillStyle = radialGradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
但如果是优雅的圆锥形渐变呢?
const grad = ctx.createConicGradient(0, 100, 100);
grad.addColorStop(0, 'red');
grad.addColorStop(0.25, 'orange');
grad.addColorStop(0.5, 'yellow');
grad.addColorStop(0.75, 'green');
grad.addColorStop(1, 'blue');
ctx.fillStyle = grad;
ctx.fillRect(0, 0, 200, 200);
文本修饰符
Canvas2Ds 的文本渲染功能远远落后。Chrome 为 Canvas2D 文本渲染添加了多个新属性:
- ctx.letterSpacing
- ctx.wordSpacingwordSpacing
- ctx.fontVariantfontVariant
- ctx.fontKerning
- ctx.fontStretchfontStretch
- ctx.textDecoration
- ctx.textUnderlinePosition
- ctx.textRendering
这些属性都与具有相同名称的 CSS 对应属性匹配。
第 2 部分:人体工程学调整
以前,Canvas2D 可以执行一些操作,但实现起来不必要地复杂。以下是一些面向想要使用 Canvas2D 的 JavaScript 开发者的改进:
上下文重置
为了说明如何清空画布,我编写了一个很有趣的绘制复古图案的函数:
draw90sPattern();
太棒了!现在,我已经完成了该图案,接下来想清除画布并绘制其他内容。
等等,我们要如何清除画布?没错!ctx.clearRect()
,当然。
ctx.clearRect(0, 0, canvas.width, canvas.height);
咦… 没用。没错!我必须先重置转换:
ctx.resetTransform();
ctx.clearRect(0, 0, canvas.width, canvas.height);
太好了!一块空白画布。现在,我们开始绘制一条漂亮的水平线:
ctx.moveTo(10, 10);
ctx.lineTo(canvas.width, 10);
ctx.stroke();
嗷!回答不正确!😡? 这个多余的行是干什么的?还有,为什么它是粉色的?好的,我们来看看 StackOverflow。
canvas.width = canvas.width;
为什么这么傻?为什么这么困难?
不过现在不一样了。借助新 API,我们实现了简单、优雅、美观且开创性的:
ctx.reset();
抱歉让您久等了。
过滤条件
SVG 滤镜是一门学问。如果您刚开始接触 SVG 滤镜,强烈建议您阅读 SVG 滤镜的艺术及其出色之处,了解 SVG 滤镜的强大潜力。
SVG 样式滤镜现已适用于 Canvas2D!您只需将滤镜作为指向页面上另一个 SVG 滤镜元素的网址传递即可:
<svg>
<defs>
<filter id="svgFilter">
<feGaussianBlur in="SourceGraphic" stdDeviation="5" />
<feConvolveMatrix kernelMatrix="-3 0 0 0 0.5 0 0 0 3" />
<feColorMatrix type="hueRotate" values="90" />
</filter>
</defs>
</svg>
const canvas = document.createElement('canvas');
canvas.width = 500;
canvas.height = 400;
const ctx = canvas.getContext('2d');
document.body.appendChild(canvas);
ctx.filter = "url('#svgFilter')";
draw90sPattern(ctx);
这会严重破坏我们的模式:
但是,如果您想执行上述操作,但不想离开 JavaScript 而去处理字符串,该怎么办? 使用新 API,这一切都是可能的。
ctx.filter = new CanvasFilter([
{ filter: 'gaussianBlur', stdDeviation: 5 },
{
filter: 'convolveMatrix',
kernelMatrix: [
[-3, 0, 0],
[0, 0.5, 0],
[0, 0, 3],
],
},
{ filter: 'colorMatrix', type: 'hueRotate', values: 90 },
]);
就是这么简单!您可以试用一下,并在此处的演示版中玩玩参数。
第 3 部分:性能提升
在推出新版 Canvas2D API 时,我们还希望尽可能提高性能。我们添加了几项功能,让开发者能够更精细地控制其网站,并实现尽可能流畅的帧速率:
会经常读取
使用 getImageData()
从画布读取返回的像素数据。该过程可能会非常慢。借助新 API,您可以明确标记要回读的画布(例如,用于生成效果)。这使您能够在后台进行优化,并使画布针对更多种类的用例快速运行。此功能已在 Firefox 中推出一段时间,我们最终将其纳入画布规范。
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
上下文丢失
让我们让悲伤的标签页重获新生!如果客户端耗尽 GPU 内存或画布发生其他灾难,您现在可以接收回调并根据需要重新绘制:
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.addEventListener('contextlost', onContextLost);
canvas.addEventListener('contextrestored', redraw);
如果您想详细了解画布上下文和损失,可以参阅 WHATWG 网站上的详细说明。
总结
无论您是刚开始接触 Canvas2D,还是已经使用多年,或者多年来一直避免使用它,我都建议您再仔细看看 Canvas。它是一直存在的“邻居 API”。