Canvas2D,就是您

亚伦·克拉耶斯基
Aaron Krajeski

面对着色器、网格和滤镜的世界,Canvas2D 或许会让您无从下手。但这确实是正确的! 30-40% 的网页包含 <canvas> 元素,98% 的画布使用 Canvas2D 渲染上下文。汽车、冰箱上和太空中都有 Canvas2D。

不可否认,在处理最先进的 2D 绘图时,该 API 稍微落后了。 幸运的是,我们一直在努力在 Canvas2D 中实现新功能,以便赶上 CSS 的步伐、简化工效学设计并提升性能。

第 1 部分:跟上 CSS 的步伐

Canvas2D 缺少一些 CSS 的绘制命令。通过这个新 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 滤镜,我强烈建议您阅读 The Art Of SVG Filters And Why It Is Awesome,其中展示了它们的一些非凡潜力。

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 的 Wiki 查看很好的说明

总结

无论您是刚接触 Canvas2D,使用它多年,还是多年未使用它,我都在这里告诉您,让画布照片焕然一新。它始终伴你左右

致谢

主打图片由 Sandie Clarke 提供,由 Unsplash 提供。