Así que mi objetivo es simple... creo. Quiero dibujar un rectángulo con un agujero cortado en el medio. Pero no quiero que ese agujero atraviese nada más en el lienzo. Ahora mismo tengo algo como esto:
context.fillStyle = 'blue'; context.fillRect(0, 0, width, height); context.fillStyle = "#000000"; context.beginPath(); context.rect(0, 0 , width, height); context.fill(); enter code here context.globalCompositeOperation="destination-out"; context.beginPath(); context.arc(width / 2, height / 2 , 80, 0, 50); context.fill();Pero esto también atraviesa el fondo, ¿cómo puedo hacer que solo corte el rectángulo negro y nada más?
Ejemplo visual en caso de que no me esté explicando bien:

¿Es esto lo que esperabas lograr?
const context = document.getElementById("canvas").getContext('2d'); const width = 100; const height = 100; const circleSize = 30; context.fillStyle = "black"; context.fillRect(0, 0 , width, height); context.beginPath(); context.arc(width / 2, height / 2, circleSize, 0, 2 * Math.PI); context.clip(); context.fillStyle = "blue"; context.fillRect(0, 0, width, height); <canvas id="canvas"/> Este artículo podría ser útil:
https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Compositing
En el ejemplo anterior, estoy usando clip . Como dice la documentación:
El método CanvasRenderingContext2D.clip() de la API Canvas 2D convierte la ruta actual o dada en la región de recorte actual.
Esto significa que cualquier elemento agregado al contexto después de esta línea, solo se dibujará dentro de esa ruta recortada.
Echa un vistazo a esto a ver si es lo que buscas:
<canvas id="canvas" width="200" height="160"></canvas> <script> class Shape { constructor(x, y, width, height, color) { this.color = color this.path = new Path2D(); this.path.arc(x, y, height / 3, 0, 2 * Math.PI); this.path.rect(x + width / 2, y - height / 2, -width, height); } draw(ctx) { ctx.beginPath(); ctx.fillStyle = this.color; ctx.fill(this.path); } } var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); shapes = []; shapes.push(new Shape(40, 40, 80, 80, "blue")); shapes.push(new Shape(60, 65, 70, 70, "red")); shapes.push(new Shape(80, 40, 65, 65, "gray")); shapes.forEach((s) => { s.draw(ctx); }); </script> Estoy usando Path2D() porque tuve un ejemplo rápido de algo que hice antes, pero eso también debería ser factible sin él. La teoría detrás es simple, solo queremos llenar el opuesto en el círculo.
Codifiqué el radio a un tercio de la altura, pero puede cambiar todo eso a otra cosa o incluso ser un parámetro que el usuario final puede cambiar