合成和裁剪
在我们之前所有 示例中,形状始终一个叠加在另一个上面绘制。这对大多数情况来说已经足够了,但它限制了组合形状的构建顺序。但是,我们可以通过设置 globalCompositeOperation
属性来改变这种行为。此外,clip
属性允许我们隐藏形状中不需要的部分。
globalCompositeOperation
我们不仅可以将新形状绘制在现有形状的后面,还可以用它来遮蔽某些区域,从画布中清除部分内容(不限于像 clearRect()
方法那样清除矩形),等等。
globalCompositeOperation = type
-
这将设置在绘制新形状时应用的合成操作类型,其中 type 是一个字符串,标识要使用的十二种合成操作中的哪一种。
裁剪路径
裁剪路径就像一个普通的画布形状,但它充当遮罩,隐藏形状中不需要的部分。下图对此进行了可视化。红色星形是我们裁剪路径。落在该路径之外的所有内容都不会绘制到画布上。
如果我们将裁剪路径与上面看到的 globalCompositeOperation
属性进行比较,我们会发现两种合成模式在 source-in
和 source-atop
中实现了或多或少相同的效果。两者之间最重要的区别在于,裁剪路径永远不会实际绘制到画布上,并且裁剪路径不会受添加新形状的影响。这使得裁剪路径成为在受限区域中绘制多个形状的理想选择。
在关于 绘制形状 的章节中,我只提到了 stroke()
和 fill()
方法,但我们可以使用路径的第三种方法,称为 clip()
。
clip()
-
将当前正在构建的路径转换为当前裁剪路径。
您使用 clip()
来代替 closePath()
关闭路径,将其转换为裁剪路径,而不是描边或填充路径。
默认情况下,<canvas>
元素的裁剪路径与画布本身的大小完全相同。换句话说,不会发生裁剪。
一个 clip
示例
在此示例中,我们将使用圆形裁剪路径将一组随机星星的绘制限制在特定区域。
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
ctx.fillRect(0, 0, 150, 150);
ctx.translate(75, 75);
// Create a circular clipping path
ctx.beginPath();
ctx.arc(0, 0, 60, 0, Math.PI * 2, true);
ctx.clip();
// Draw background
const lingrad = ctx.createLinearGradient(0, -75, 0, 75);
lingrad.addColorStop(0, "#232256");
lingrad.addColorStop(1, "#143778");
ctx.fillStyle = lingrad;
ctx.fillRect(-75, -75, 150, 150);
generateStars(ctx);
}
function generateStars(ctx) {
for (let j = 1; j < 50; j++) {
ctx.save();
ctx.fillStyle = "#fff";
ctx.translate(
75 - Math.floor(Math.random() * 150),
75 - Math.floor(Math.random() * 150),
);
drawStar(ctx, Math.floor(Math.random() * 4) + 2);
ctx.restore();
}
}
function drawStar(ctx, r) {
ctx.save();
ctx.beginPath();
ctx.moveTo(r, 0);
for (let i = 0; i < 9; i++) {
ctx.rotate(Math.PI / 5);
if (i % 2 === 0) {
ctx.lineTo((r / 0.525731) * 0.200811, 0);
} else {
ctx.lineTo(r, 0);
}
}
ctx.closePath();
ctx.fill();
ctx.restore();
}
在代码的前几行中,我们绘制了一个与画布大小相同的黑色矩形作为背景,然后将原点平移到中心。接下来,我们通过绘制一个圆弧并调用 clip()
来创建圆形裁剪路径。裁剪路径也是画布保存状态的一部分。如果我们想保留原始裁剪路径,我们可以在创建新的裁剪路径之前保存画布状态。
在创建裁剪路径之后绘制的所有内容只会在该路径内部出现。您可以在接下来绘制的线性渐变中清楚地看到这一点。在此之后,使用自定义的 drawStar()
函数绘制了一组 50 个随机定位和缩放的星星。同样,星星只会在定义的裁剪路径内部出现。
反向裁剪路径
不存在反向裁剪遮罩。但是,我们可以定义一个遮罩,它用矩形填充整个画布,并在想要跳过的地方有一个孔。当 绘制有孔的形状 时,我们需要按与外形相反的方向绘制孔。在下面的示例中,我们在天空中打了一个洞。
矩形没有绘制方向,但它表现得好像我们以顺时针方向绘制它一样。默认情况下,圆弧命令也是以顺时针方向进行,但我们可以使用最后一个参数来改变它的方向。
function draw() {
const canvas = document.getElementById("canvas");
if (canvas.getContext) {
const ctx = canvas.getContext("2d");
ctx.translate(75, 75);
// Clipping path
ctx.beginPath();
ctx.rect(-75, -75, 150, 150); // Outer rectangle
ctx.arc(0, 0, 60, 0, Math.PI * 2, true); // Hole anticlockwise
ctx.clip();
// Draw background
const lingrad = ctx.createLinearGradient(0, -75, 0, 75);
lingrad.addColorStop(0, "#232256");
lingrad.addColorStop(1, "#143778");
ctx.fillStyle = lingrad;
ctx.fillRect(-75, -75, 150, 150);
generateStars(ctx);
}
}