Search code examples
javascripthtml5-canvas

How to make only two shapes compost together in HTML canvas without effecting other shapes


So my goal is simple... I think. I want to draw a rectangle with a hole cut out in the middle. But I don't want that hole to go through anything else on the canvas. Right now I have something like this:

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();

But this also cuts through the background as well, how would I make it only cut through the black rectangle and nothing else?

Visual Example in case I'm not explaining well:

Visual Example in case im not explaining well


Solution

  • Take a look at this see if that is what you are looking for:

    <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>

    I'm using a Path2D() because I had a quick example from something I did before, but that should be doable without it too. The theory behind is simple, we just want to fill the opposite on the circle.

    I hard coded the radius to a third of the height, but you can change all that to something else or even be a parameter the final user can change