面对着色器、网格和滤镜的世界,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 文本渲染添加了几个新属性:
- ctx.letterSpacing
- ctx.wordSpacing
- ctx.fontVariant
- ctx.fontKerning
- ctx.fontStretch
- 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 滤镜,我强烈建议您阅读 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 提供。