如何在p5.js中使用形状作为其他形状的遮罩或裁剪路径?
在p5.js中用形状作为遮罩/裁剪路径的实现方法
嘿,这个问题我刚好踩过坑!p5.js的mask()方法确实只针对图片对象,没法直接给形状用,但咱们可以借助Canvas底层API或者图形缓冲区来实现,下面给你分享三种实用方案:
方法1:直接用Canvas上下文的clip()函数(最简便)
p5.js基于Canvas API,所以咱们可以直接调用底层的clip()方法,它会把当前绘制的形状作为裁剪路径,之后所有绘制内容都会被这个形状“框住”。
基础示例:用矩形裁剪随机圆形
function setup() { createCanvas(400, 400); } function draw() { background(220); // 第一步:先画你要当遮罩的形状(这里是一个居中矩形) noFill(); // 遮罩形状不需要填充,只要路径就行 rect(100, 100, 200, 200); // 调用clip(),开启裁剪模式 drawingContext.clip(); // 第二步:绘制需要被裁剪的内容(一堆随机红色圆形) fill(255, 0, 0); noStroke(); for(let i = 0; i < 50; i++){ circle(random(width), random(height), 30); } }
进阶:控制裁剪范围(避免影响后续绘制)
如果之后还要画其他不受裁剪影响的内容,记得用save()和restore()保存/恢复绘图状态:
function draw() { background(220); // 保存当前绘图状态(包括裁剪、颜色等设置) drawingContext.save(); // 定义遮罩形状并开启裁剪 noFill(); rect(100, 100, 200, 200); drawingContext.clip(); // 绘制被裁剪的内容 fill(255, 0, 0); noStroke(); for(let i = 0; i < 50; i++){ circle(random(width), random(height), 30); } // 恢复之前的绘图状态,关闭裁剪 drawingContext.restore(); // 这里画的内容就不会被裁剪啦(比如一个外圈大圆) stroke(0); noFill(); circle(200, 200, 350); }
方法2:用createGraphics()生成缓冲区做遮罩(最灵活)
这种方法适合复杂遮罩(比如不规则形状、渐变遮罩)或者需要重复使用遮罩的场景。核心思路是把形状画到一个图形缓冲区里,转成图片后用mask()方法(毕竟图片支持mask)。
示例:用自定义形状做遮罩
let maskBuffer; // 保存遮罩的图形缓冲区 function setup() { createCanvas(400, 400); // 创建和画布同尺寸的缓冲区 maskBuffer = createGraphics(width, height); maskBuffer.background(0, 0); // 背景设为透明 maskBuffer.fill(255); // 遮罩区域用白色填充(白色对应显示内容,透明对应隐藏) // 在缓冲区里画遮罩形状(比如一个五角星) maskBuffer.beginShape(); maskBuffer.vertex(200, 80); maskBuffer.vertex(230, 180); maskBuffer.vertex(320, 180); maskBuffer.vertex(250, 240); maskBuffer.vertex(270, 340); maskBuffer.vertex(200, 280); maskBuffer.vertex(130, 340); maskBuffer.vertex(150, 240); maskBuffer.vertex(80, 180); maskBuffer.vertex(170, 180); maskBuffer.endShape(CLOSE); } function draw() { background(220); // 创建要被遮罩的内容(这里用另一个缓冲区画线条) let contentBuffer = createGraphics(width, height); contentBuffer.stroke(255, 0, 0); contentBuffer.strokeWeight(2); for(let i = 0; i < 100; i++){ contentBuffer.line(random(width), random(height), random(width), random(height)); } // 将缓冲区转成图片,应用遮罩 let contentImg = contentBuffer.get(); contentImg.mask(maskBuffer.get()); // 把遮罩后的内容画到画布上 image(contentImg, 0, 0); }
小提示:如果要做半透明遮罩,可以把缓冲区里的填充色改成灰色(比如fill(127)),灰度值对应遮罩的透明度,越黑越透明。
方法3:用blendMode()快速实现(适合简单场景)
如果只是需要快速实现“只显示重叠区域”的效果,可以用混合模式,比如DST_IN,不过这个方法局限性较大,不如前两种灵活。
示例:用圆形遮罩渐变背景
function draw() { // 先画内容:一个从红到蓝的渐变背景 for(let y = 0; y < height; y++){ let r = map(y, 0, height, 255, 0); let b = map(y, 0, height, 0, 255); stroke(r, 0, b); line(0, y, width, y); } // 设置混合模式为DST_IN:只保留内容和遮罩重叠的部分 blendMode(DST_IN); // 画遮罩形状(填充颜色不影响,只要有像素即可) fill(255); circle(200, 200, 200); // 恢复默认混合模式 blendMode(BLEND); }
注意:这个方法需要先画内容再画遮罩,而且混合模式的效果可能和预期有差异,比如遮罩半透明时会让内容也变透明。
内容的提问来源于stack exchange,提问作者amcc
相关产品推荐
相关产品推荐

