Canvas2D,就是您

Aaron Krajeski
Aaron Krajeski

在着色器、网格和滤镜的世界中,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 文本渲染添加了多个新属性:

这些属性都与具有相同名称的 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”。