使用 canvas 绘制形状
既然我们已经设置好 canvas 环境,我们就可以深入了解如何在 canvas 上绘图了。通过本文,你将学会如何绘制矩形、三角形、线条、弧线和曲线,熟悉一些基本图形。在使用 canvas 绘制对象时,路径是必不可少的,我们将了解如何完成这项工作。
网格
在我们开始绘图之前,我们需要讨论 canvas 网格或坐标空间。上一页的 HTML 骨架中有一个 canvas 元素,宽 150 像素,高 150 像素。

通常,网格中的 1 个单位对应于 canvas 上的 1 个像素。此网格的原点位于左上角,坐标为 (0,0)。所有元素都相对于此原点放置。因此,蓝色方块左上角的坐标为 (x,y),即距离左侧 x 像素,距离顶部 y 像素。在本教程的后面,我们将看到如何将原点平移到不同的位置,旋转网格,甚至缩放它,但现在我们将坚持使用默认设置。
绘制矩形
与 SVG 不同,<canvas> 只支持两种基本形状:矩形和路径(由线段连接的点列表)。所有其他形状都必须通过组合一个或多个路径来创建。幸运的是,我们有各种路径绘制函数,可以创建非常复杂的形状。
首先让我们看看矩形。有三个函数可以在 canvas 上绘制矩形
fillRect(x, y, width, height)-
绘制一个填充矩形。
strokeRect(x, y, width, height)-
绘制一个矩形轮廓。
clearRect(x, y, width, height)-
清除指定的矩形区域,使其完全透明。
这三个函数都接受相同的参数。x 和 y 指定矩形左上角在 canvas 上的位置(相对于原点)。width 和 height 提供矩形的大小。
下面是上一页的 draw() 函数,但现在它使用了这三个函数。
矩形形状示例
function draw() {
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
ctx.fillRect(25, 25, 100, 100);
ctx.clearRect(45, 45, 60, 60);
ctx.strokeRect(50, 50, 50, 50);
}
此示例的输出如下所示。
fillRect() 函数绘制一个边长为 100 像素的大黑色正方形。然后 clearRect() 函数从中心清除一个 60x60 像素的正方形,然后调用 strokeRect() 在清除的正方形内创建一个 50x50 像素的矩形轮廓(概念上是 50x50;实际上是 52x52,下一节将解释)。
在接下来的页面中,我们将看到 clearRect() 的两种替代方法,我们还将看到如何更改渲染形状的颜色和描边样式。
与我们将在下一节中看到的路径函数不同,所有三个矩形函数都立即绘制到 canvas 上。
边缘模糊?
在上面的矩形示例以及所有即将到来的示例中,你可能会注意到形状的边缘可能比用 SVG 或 CSS 绘制的等效形状显得模糊。这并不是因为 canvas API 无法绘制锐利的边缘,而是因为 canvas 网格映射到屏幕上的实际像素的方式,以及在某些情况下,因为浏览器缩放 canvas 的方式。如果上述示例不够明显,让我们使用 CSS 放大 canvas
<canvas id="canvas" width="15" height="15"></canvas>
#canvas {
width: 300px;
height: 300px;
}
function draw() {
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
ctx.strokeRect(2, 2, 10, 10);
ctx.fillRect(7, 7, 1, 1);
}
在此示例中,我们创建了一个非常小的 canvas (15x15),但随后使用 CSS 将其放大到 300x300 像素。结果,每个 canvas 像素现在由一个 20x20 的屏幕像素块表示。我们从 (2,2) 绘制一个描边矩形到 (12,12),并从 (7,7) 绘制一个填充矩形到 (8,8)。它看起来真的很模糊。这是因为默认情况下,当浏览器缩放栅格图像时,它会使用平滑算法来插值额外的像素。这对于照片或具有卷曲边缘的 canvas 图形非常有用,但对于直边形状则不太理想。为了解决这个问题,我们可以将 image-rendering 设置为 pixelated
#canvas {
image-rendering: pixelated;
}
现在,当浏览器缩放 canvas 时,它会尽可能地保留原始图像的像素化。
注意: image-rendering: pixelated 作为一种保留清晰边缘的技术并非没有问题。当 CSS 像素与设备像素不一致时(如果 devicePixelRatio 不是整数),某些像素可能会绘制得比其他像素大,从而导致外观不均匀。然而,这不是一个容易解决的问题,因为当 CSS 像素无法准确映射到设备像素时,不可能精确地填充设备像素。
但现在出现了另一个问题,你也可以在原始矩形示例中观察到:描边矩形不仅宽 2 像素而不是 1 像素,而且看起来是灰色而不是默认的黑色。这是因为坐标被解释为形状边界的方式。
如果再次查看上面的网格图,你会发现像 2 或 12 这样的坐标并不是标识一个像素,而是标识两个像素之间的边缘。在下面的图像中,网格表示 canvas 坐标网格。网格线之间的方块是屏幕上的实际像素。在下面的第一个网格图像中,一个从 (2,1) 到 (5,5) 的矩形被填充。它们之间的整个区域(浅红色)落在像素边界上,因此最终填充的矩形将具有清晰的边缘。

如果您考虑一条从 (3,1) 到 (3,5) 的路径,线宽为 1.0,您将遇到第二张图片中的情况。要填充的实际区域(深蓝色)只延伸到路径两侧像素的一半。必须渲染此区域的近似值,这意味着这些像素仅部分着色,并导致整个区域(浅蓝色和深蓝色)填充的颜色仅为实际描边颜色的一半深。这就是在上面的矩形示例中 strokeRect() 调用中 1.0 宽度线发生的情况。
为了解决这个问题,你必须在创建路径时非常精确。知道 1.0 宽度的线条将向路径两侧延伸半个单位,从像素的中心创建路径将导致第三张图片中的情况——1.0 线宽最终将完全精确地填充单个像素的垂直线。
注意:请注意,在我们的垂直线示例中,Y 位置仍然引用了一个整数网格线位置——如果不是这样,我们会在端点处看到半覆盖的像素。
所以这就是我们前面说矩形示例中的 strokeRect(50, 50, 50, 50) 调用在概念上是 50x50,但实际上是 52x52 的原因。轮廓的实际填充区域从 (49.5, 49.5) 开始,到 (100.5, 100.5) 结束,由于部分填充的像素,实际填充区域是从 (49,49) 到 (101,101),即 52x52,并且边缘宽 2 像素。要获得一个完全 50x50 的实心 1 像素宽轮廓,你需要将矩形缩小轮廓的厚度 (1px),并将其移动轮廓厚度的一半 (0.5px)
function draw() {
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
ctx.strokeRect(2.5, 2.5, 9, 9);
ctx.fillRect(7, 7, 1, 1);
}
对于偶数宽度的线条,每一半最终都是整数个像素,因此您需要一条位于像素之间的路径(即 (3,1) 到 (3,5)),而不是位于像素中间的路径。
尽管最初处理可伸缩 2D 图形时会有些痛苦,但关注像素网格和路径位置可确保无论涉及缩放或任何其他变换,您的绘图都看起来正确。在正确位置绘制的 1.0 宽度垂直线在放大 2 倍时将变为清晰的 2 像素线,并出现在正确位置。
这种部分填充像素的现象也延伸到不与像素网格对齐的形状。例如,考虑一个旋转的矩形(你将在下一节学习如何绘制它)。为了看看有和没有 image-rendering: pixelated 的效果,我们并排放置了两个 canvas,还有一个以全尺寸绘制的 canvas,带网格线
function draw(canvasId) {
const canvas = document.getElementById(canvasId);
const ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(3, 2);
ctx.lineTo(9, 4.5);
ctx.lineTo(6.5, 10.5);
ctx.lineTo(0.5, 8);
ctx.closePath();
ctx.fill();
}
function drawFullScale() {
const canvas = document.getElementById("canvas3");
const ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(60, 40);
ctx.lineTo(180, 90);
ctx.lineTo(130, 210);
ctx.lineTo(10, 160);
ctx.closePath();
ctx.fill();
ctx.strokeStyle = "lightgray";
for (let i = 0; i < 16; i++) {
ctx.moveTo(i * 20, 0);
ctx.lineTo(i * 20, 300);
ctx.moveTo(0, i * 20);
ctx.lineTo(300, i * 20);
ctx.stroke();
}
}
如果放大图像会使其看起来比预期更模糊,那么缩小图像会使其看起来更清晰。例如,如果您想让 canvas 在屏幕上显示为 300x150 像素,您可以将其创建为 600x300 像素,然后使用 CSS 将其缩小。这在高清屏幕(例如苹果的 Retina 显示器)上特别有用,其中一个 CSS 像素由多个屏幕像素表示,因此如果您忠实地绘制一个 300x150 像素的 canvas,它将不会具有与页面上其他元素相同的像素分辨率。
绘制路径
现在让我们看看路径。路径是点的列表,由不同形状的线段连接,可以是弯曲的或不弯曲的,具有不同的宽度和不同的颜色。路径,甚至子路径,都可以闭合。要使用路径创建形状,我们需要采取一些额外的步骤
- 首先,创建路径。
- 然后使用绘图命令在路径中绘图。
- 路径创建后,您可以描边或填充路径以进行渲染。
以下是用于执行这些步骤的函数
beginPath()-
创建一个新路径。创建后,未来的绘图命令将指向该路径并用于构建该路径。
- 路径方法
-
设置对象不同路径的方法。
closePath()-
向路径添加一条直线,连接到当前子路径的起点。
stroke()-
通过描边轮廓来绘制形状。
fill()-
通过填充路径内容区域绘制实心形状。
创建路径的第一步是调用 beginPath()。在内部,路径存储为子路径(直线、弧线等)列表,它们共同构成一个形状。每次调用此方法时,列表都会重置,我们可以开始绘制新形状。
注意:当当前路径为空时,例如在调用 beginPath() 之后或在新创建的 canvas 上,第一个路径构造命令始终被视为 moveTo(),无论它实际是什么。因此,在重置路径后,您几乎总是希望明确设置起始位置。
第二步是调用实际指定要绘制路径的方法。我们很快就会看到这些。
第三步(可选)是调用 closePath()。此方法尝试通过从当前点到起点绘制一条直线来闭合形状。如果形状已闭合或列表中只有一个点,则此函数不执行任何操作。
注意:当您调用 fill() 时,任何开放形状都会自动闭合,因此您不必调用 closePath()。但当您调用 stroke() 时,情况并非如此。
绘制三角形
例如,绘制三角形的代码可能看起来像这样
function draw() {
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(75, 50);
ctx.lineTo(100, 75);
ctx.lineTo(100, 25);
ctx.fill();
}
结果如下所示
移动画笔
一个非常有用的函数,它实际上不绘制任何东西,但成为上面描述的路径列表的一部分,是 moveTo() 函数。你最好把它想象成把钢笔或铅笔从一张纸上的一个点抬起来,然后放到下一个点。
moveTo(x, y)-
将画笔移动到由
x和y指定的坐标。
当 canvas 初始化或调用 beginPath() 时,您通常会希望使用 moveTo() 函数将起点放置在其他位置。我们也可以使用 moveTo() 绘制不连接的路径。看看下面的笑脸。
要自己尝试一下,您可以使用下面的代码片段。只需将其粘贴到我们前面看到的 draw() 函数中即可。
function draw() {
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.arc(75, 75, 50, 0, Math.PI * 2, true); // Outer circle
ctx.moveTo(110, 75);
ctx.arc(75, 75, 35, 0, Math.PI, false); // Mouth (clockwise)
ctx.moveTo(65, 65);
ctx.arc(60, 65, 5, 0, Math.PI * 2, true); // Left eye
ctx.moveTo(95, 65);
ctx.arc(90, 65, 5, 0, Math.PI * 2, true); // Right eye
ctx.stroke();
}
结果如下所示
如果你想看到连接线,可以删除调用 moveTo() 的行。
注意:要了解有关 arc() 函数的更多信息,请参阅下面的弧线部分。
直线
要绘制直线,请使用 lineTo() 方法。
lineTo(x, y)-
从当前绘图位置到由
x和y指定的位置绘制一条直线。
此方法接受两个参数,x 和 y,它们是线段终点的坐标。起点取决于先前绘制的路径,前一个路径的终点是后续路径的起点,依此类推。起点也可以通过使用 moveTo() 方法进行更改。
下面的示例绘制了两个三角形,一个填充,一个描边。
function draw() {
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
// Filled triangle
ctx.beginPath();
ctx.moveTo(25, 25);
ctx.lineTo(105, 25);
ctx.lineTo(25, 105);
ctx.fill();
// Stroked triangle
ctx.beginPath();
ctx.moveTo(125, 125);
ctx.lineTo(125, 45);
ctx.lineTo(45, 125);
ctx.closePath();
ctx.stroke();
}
这通过调用 beginPath() 开始一个新的形状路径。然后我们使用 moveTo() 方法将起点移动到所需位置。在这下面,绘制了两条线,构成三角形的两条边。
您会注意到填充三角形和描边三角形之间的区别。如上所述,这是因为当路径填充时形状会自动闭合,但当它们被描边时则不会。如果我们遗漏了描边三角形的 closePath(),那么只会绘制两条线,而不是一个完整的三角形。
弧线
要绘制弧线或圆形,我们使用 arc() 或 arcTo() 方法。
arc(x, y, radius, startAngle, endAngle, counterclockwise)-
绘制一个以 (x, y) 位置为中心,半径为 r,从 startAngle 开始,到 endAngle 结束的弧,方向由 counterclockwise 指示(默认为顺时针)。
arcTo(x1, y1, x2, y2, radius)-
绘制一个带有给定控制点和半径的弧,通过直线连接到前一个点。
让我们更详细地了解一下 arc 方法,它接受六个参数:x 和 y 是弧线所在圆的中心的坐标。radius 不言自明。startAngle 和 endAngle 参数以弧度定义弧线在圆曲线上的起点和终点。这些是从 x 轴测量的。counterclockwise 参数是一个布尔值,当为 true 时,弧线逆时针绘制;否则,弧线顺时针绘制。
注意:arc 函数中的角度以弧度而不是度数测量。要将度数转换为弧度,您可以使用以下 JavaScript 表达式:radians = (Math.PI/180)*degrees。
下面的例子比我们上面看到的例子稍微复杂一些。它绘制了 12 个具有不同角度和填充的不同弧。
这两个 for 循环用于遍历弧的行和列。对于每个弧,我们通过调用 beginPath() 开始一个新路径。在代码中,弧的每个参数都放在一个变量中以提高清晰度,但您在实际生活中不一定会这样做。
x 和 y 坐标应该足够清楚。radius 和 startAngle 是固定的。endAngle 在第一列从 180 度(半圆)开始,并以 90 度步长增加,最终在最后一列形成一个完整的圆。
clockwise 参数的语句导致第一行和第三行绘制为顺时针弧,第二行和第四行绘制为逆时针弧。最后,if 语句使上半部分为描边弧,下半部分为填充弧。
注意:此示例需要比本页其他示例稍大的 canvas:150 x 200 像素。
function draw() {
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
for (let i = 0; i < 4; i++) {
for (let j = 0; j < 3; j++) {
ctx.beginPath();
const x = 25 + j * 50; // x coordinate
const y = 25 + i * 50; // y coordinate
const radius = 20; // Arc radius
const startAngle = 0; // Starting point on circle
const endAngle = Math.PI + (Math.PI * j) / 2; // End point on circle
const counterclockwise = i % 2 !== 0; // clockwise or counterclockwise
ctx.arc(x, y, radius, startAngle, endAngle, counterclockwise);
if (i > 1) {
ctx.fill();
} else {
ctx.stroke();
}
}
}
}
贝塞尔曲线和二次曲线
下一种可用的路径类型是 贝塞尔曲线,有三次和二次两种类型。这些通常用于绘制复杂的有机形状。
quadraticCurveTo(cp1x, cp1y, x, y)-
从当前画笔位置到由
x和y指定的终点绘制二次贝塞尔曲线,使用由cp1x和cp1y指定的控制点。 bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)-
从当前画笔位置到由
x和y指定的终点绘制三次贝塞尔曲线,使用由 (cp1x,cp1y) 和 (cp2x,cp2y) 指定的控制点。
这两者之间的区别在于,二次贝塞尔曲线有一个起点和终点(蓝色点)和一个控制点(由红色点表示),而三次贝塞尔曲线使用两个控制点。
这两个方法中的 x 和 y 参数是终点的坐标。cp1x 和 cp1y 是第一个控制点的坐标,cp2x 和 cp2y 是第二个控制点的坐标。
使用二次和三次贝塞尔曲线可能相当具有挑战性,因为与 Adobe Illustrator 等矢量绘图软件不同,我们没有直接的视觉反馈来了解我们正在做什么。这使得绘制复杂形状变得相当困难。在下面的示例中,我们将绘制一些简单的有机形状,但如果您有时间,最重要的是有耐心,则可以创建更复杂的形状。
这些例子没什么特别难的。在这两种情况下,我们都看到了一系列曲线的绘制,最终形成了一个完整的形状。
二次贝塞尔曲线
此示例使用多个二次贝塞尔曲线渲染一个语音气泡。
function draw() {
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
// Quadratic curves example
ctx.beginPath();
ctx.moveTo(75, 25);
ctx.quadraticCurveTo(25, 25, 25, 62.5);
ctx.quadraticCurveTo(25, 100, 50, 100);
ctx.quadraticCurveTo(50, 120, 30, 125);
ctx.quadraticCurveTo(60, 120, 65, 100);
ctx.quadraticCurveTo(125, 100, 125, 62.5);
ctx.quadraticCurveTo(125, 25, 75, 25);
ctx.stroke();
}
三次贝塞尔曲线
此示例使用三次贝塞尔曲线绘制一个心形。
function draw() {
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
// Cubic curves example
ctx.beginPath();
ctx.moveTo(75, 40);
ctx.bezierCurveTo(75, 37, 70, 25, 50, 25);
ctx.bezierCurveTo(20, 25, 20, 62.5, 20, 62.5);
ctx.bezierCurveTo(20, 80, 40, 102, 75, 120);
ctx.bezierCurveTo(110, 102, 130, 80, 130, 62.5);
ctx.bezierCurveTo(130, 62.5, 130, 25, 100, 25);
ctx.bezierCurveTo(85, 25, 75, 37, 75, 40);
ctx.fill();
}
矩形
除了我们在绘制矩形中看到的三种直接在画布上绘制矩形的方法外,还有 rect() 方法,它将一个矩形路径添加到当前开放的路径中。
rect(x, y, width, height)-
绘制一个矩形,其左上角由 (
x,y) 指定,并具有指定的width和height。
在执行此方法之前,会自动调用 moveTo() 方法,参数为 (x,y)。换句话说,当前画笔位置会自动重置为默认坐标。
组合
到目前为止,此页面上的每个示例都只使用每种形状的一种路径函数。然而,您可以使用创建形状的路径的数量或类型没有限制。因此,在这个最终示例中,让我们结合所有路径函数来制作一组非常著名的游戏角色。
function draw() {
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
roundedRect(ctx, 12, 12, 184, 168, 15);
roundedRect(ctx, 19, 19, 170, 154, 9);
roundedRect(ctx, 53, 53, 49, 33, 10);
roundedRect(ctx, 53, 119, 49, 16, 6);
roundedRect(ctx, 135, 53, 49, 33, 10);
roundedRect(ctx, 135, 119, 25, 49, 10);
ctx.beginPath();
ctx.arc(37, 37, 13, Math.PI / 7, -Math.PI / 7, false);
ctx.lineTo(31, 37);
ctx.fill();
for (let i = 0; i < 8; i++) {
ctx.fillRect(51 + i * 16, 35, 4, 4);
}
for (let i = 0; i < 6; i++) {
ctx.fillRect(115, 51 + i * 16, 4, 4);
}
for (let i = 0; i < 8; i++) {
ctx.fillRect(51 + i * 16, 99, 4, 4);
}
ctx.beginPath();
ctx.moveTo(83, 116);
ctx.lineTo(83, 102);
ctx.bezierCurveTo(83, 94, 89, 88, 97, 88);
ctx.bezierCurveTo(105, 88, 111, 94, 111, 102);
ctx.lineTo(111, 116);
ctx.lineTo(106.333, 111.333);
ctx.lineTo(101.666, 116);
ctx.lineTo(97, 111.333);
ctx.lineTo(92.333, 116);
ctx.lineTo(87.666, 111.333);
ctx.lineTo(83, 116);
ctx.fill();
ctx.fillStyle = "white";
ctx.beginPath();
ctx.moveTo(91, 96);
ctx.bezierCurveTo(88, 96, 87, 99, 87, 101);
ctx.bezierCurveTo(87, 103, 88, 106, 91, 106);
ctx.bezierCurveTo(94, 106, 95, 103, 95, 101);
ctx.bezierCurveTo(95, 99, 94, 96, 91, 96);
ctx.moveTo(103, 96);
ctx.bezierCurveTo(100, 96, 99, 99, 99, 101);
ctx.bezierCurveTo(99, 103, 100, 106, 103, 106);
ctx.bezierCurveTo(106, 106, 107, 103, 107, 101);
ctx.bezierCurveTo(107, 99, 106, 96, 103, 96);
ctx.fill();
ctx.fillStyle = "black";
ctx.beginPath();
ctx.arc(101, 102, 2, 0, Math.PI * 2, true);
ctx.fill();
ctx.beginPath();
ctx.arc(89, 102, 2, 0, Math.PI * 2, true);
ctx.fill();
}
// A utility function to draw a rectangle with rounded corners.
function roundedRect(ctx, x, y, width, height, radius) {
ctx.beginPath();
ctx.moveTo(x, y + radius);
ctx.arcTo(x, y + height, x + radius, y + height, radius);
ctx.arcTo(x + width, y + height, x + width, y + height - radius, radius);
ctx.arcTo(x + width, y, x + width - radius, y, radius);
ctx.arcTo(x, y, x, y + radius, radius);
ctx.stroke();
}
生成的图像如下所示
我们不会详细介绍这一点,因为它实际上出奇地简单。最重要的事情是注意绘图上下文上 fillStyle 属性的使用,以及实用函数(在本例中为 roundedRect())的使用。对于您经常做的绘图位使用实用函数非常有帮助,并且可以减少您需要的代码量及其复杂性。
我们将在本教程的后面更详细地介绍 fillStyle。在这里,我们所做的只是用它将路径的填充颜色从默认的黑色更改为白色,然后再更改回来。
带孔的形状
要绘制带孔的形状,我们需要在绘制外部形状时以不同的顺时针方向绘制孔。我们或者顺时针绘制外部形状,逆时针绘制内部形状;或者逆时针绘制外部形状,顺时针绘制内部形状。
function draw() {
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
ctx.beginPath();
// Outer shape clockwise ⟳
ctx.moveTo(0, 0);
ctx.lineTo(150, 0);
ctx.lineTo(75, 129.9);
// Inner shape anticlockwise ↺
ctx.moveTo(75, 20);
ctx.lineTo(50, 60);
ctx.lineTo(100, 60);
ctx.fill();
}
在上面的例子中,外三角形顺时针方向(移动到左上角,然后画一条线到右上角,最后到底部),内三角形逆时针方向(移动到顶部,然后画一条线到左下角,最后到右下角)。
Path2D 对象
正如我们在上一个示例中看到的那样,可以有一系列路径和绘图命令来在 canvas 上绘制对象。为了简化代码和提高性能,浏览器最新版本中提供的 Path2D 对象允许您缓存或记录这些绘图命令。您可以快速回放您的路径。让我们看看如何构造一个 Path2D 对象
new Path2D(); // empty path object
new Path2D(path); // copy from another Path2D object
new Path2D(d); // path from SVG path data
所有路径方法,如 moveTo、rect、arc 或 quadraticCurveTo 等,我们上面了解到的,都可以在 Path2D 对象上使用。
Path2D API 还增加了一种使用 addPath 方法组合路径的方法。这在您想从多个组件构建对象时很有用,例如。
Path2D.addPath(path [, transform])-
将路径添加到当前路径,带可选的变换矩阵。
Path2D 示例
在这个示例中,我们创建了一个矩形和一个圆形。两者都存储为 Path2D 对象,以便它们可以供以后使用。随着新的 Path2D API 的出现,一些方法已更新,可以可选地接受 Path2D 对象以代替当前路径。例如,这里使用带有路径参数的 stroke 和 fill 将这两个对象绘制到 canvas 上。
function draw() {
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const rectangle = new Path2D();
rectangle.rect(10, 10, 50, 50);
const circle = new Path2D();
circle.arc(100, 35, 25, 0, 2 * Math.PI);
ctx.stroke(rectangle);
ctx.fill(circle);
}
使用 SVG 路径
新的 canvas Path2D API 的另一个强大功能是使用 SVG 路径数据来初始化 canvas 上的路径。这可能允许您在 SVG 和 canvas 中传递和重用路径数据。
路径将移动到点 (M10 10),然后水平向右移动 80 个点 (h 80),然后向下移动 80 个点 (v 80),然后向左移动 80 个点 (h -80),然后回到起点 (z)。您可以在 Path2D 构造函数页面上查看此示例。
const p = new Path2D("M10 10 h 80 v 80 h -80 Z");