p5.js如何在不修改背景、不使用额外canvas的前提下给形状打孔
无需额外context的p5.js画布打孔实现方案
完全可以直接在默认画布实现需求,不需要创建额外的离屏canvas,不会产生额外内存占用、色差问题,以下是两种可直接运行的实现方案:
方案1:反向裁剪法(推荐)
利用p5的路径裁剪能力,先构造带孔洞的裁剪区域,再绘制前景内容,前景只会显示在裁剪区域内,孔洞位置不会绘制任何内容,完全不会影响已完成的背景。
function setup() { createCanvas(600,600); background(255, 0, 0); noStroke(); } function draw() { // 先绘制所有不需要被打孔的背景内容 fill("blue"); rect(20,20,500,500); push(); // 保存画布状态,避免裁剪影响后续绘制 // 构造带孔洞的裁剪路径:外层为全屏矩形,内层为所有需要打孔的形状 beginShape(); // 外层顺时针绘制全屏矩形 vertex(0, 0); vertex(width, 0); vertex(width, height); vertex(0, height); // 第一个孔洞(逆时针绘制,要求与外层路径方向相反) beginContour(); for(let a = 0; a < TAU; a += 0.01) { let x = 140 + cos(a) * 75; let y = 140 + sin(a) * 75; vertex(x, y); } endContour(CLOSE); // 第二个孔洞 beginContour(); for(let a = 0; a < TAU; a += 0.01) { let x = 180 + cos(a) * 75; let y = 180 + sin(a) * 75; vertex(x, y); } endContour(CLOSE); endShape(CLOSE); // 应用裁剪,后续绘制仅在裁剪区域生效 clip(); // 绘制前景内容,自动跳过孔洞区域 fill("green"); rect(100, 100, 200, 200); fill("yellow"); rect(80, 80, 100, 300); pop(); // 恢复状态,取消裁剪 noLoop(); }
方案2:混合模式法
使用DESTINATION_OUT混合模式,直接删除前景对应位置的像素,不会影响已绘制的背景,代码更简洁。
function setup() { createCanvas(600,600); background(255, 0, 0); noStroke(); } function draw() { // 绘制背景 fill("blue"); rect(20,20,500,500); push(); // 第一步:绘制所有前景内容 fill("green"); rect(100, 100, 200, 200); fill("yellow"); rect(80, 80, 100, 300); // 切换混合模式:后续绘制的形状会删除同位置已有的像素 blendMode(DESTINATION_OUT); // 绘制所有孔洞形状 fill(255); // 填充色不影响效果,只要alpha不为0即可 circle(140, 140, 150); circle(180, 180, 150); pop(); // 恢复默认混合模式,避免影响后续绘制 noLoop(); }
方案对比
- 反向裁剪法:无混合模式兼容风险,适合孔洞形状复杂、后续有多层绘制的场景,仅需要注意孔洞路径的绘制方向与外层路径相反。
- 混合模式法:代码更简洁,适合逻辑简单的场景,需要严格控制绘制顺序,必须用
push()/pop()包裹避免混合模式污染后续绘制逻辑。
内容的提问来源于stack exchange,提问作者Matty m
相关产品推荐
相关产品推荐

