0

I want to use javascript to shade in specific parts of canvas drawings on click. Here is my code below that draws a square inside a circle.

<!DOCTYPE HTML>
<html>
    <head>
    </head>
    <body>
    
     <canvas width="300" height="300" id="myCanvas" style="border:1px solid #000000;"></canvas>
     <script>
        const canvas = document.getElementById('myCanvas');
        const context = canvas.getContext('2d');
        const centerX = canvas.width / 2;
        const centerY = canvas.height / 2;
        const radius = 70;

        context.beginPath();
        context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);

        context.rect(centerX-25, centerY-25, 50, 50)
        context.lineWidth = 5;
        context.strokeStyle = '#003300';
        context.stroke();
     </script>
    </body>
      
</html>

My canvas has this: Original

And after clicking on the area outside the square but still inside the circle, I want to just shade that part in and have something like this:

Wanted result

Allen Ye
  • 27
  • 2
  • 13
  • 2
    Duplicate: [How can I perform flood fill with HTML Canvas?](https://stackoverflow.com/questions/2106995/how-can-i-perform-flood-fill-with-html-canvas) –  Oct 07 '21 at 22:18
  • Is it important that the contents inside the square is never touched? If not, you could redraw the circle/square but with the circle filled in green and the square filled in white. – DemiPixel Oct 07 '21 at 22:18
  • Thank you both for the responses. I will try the flood fill method since it is more scalable. – Allen Ye Oct 07 '21 at 22:25
  • Don't use a flood-fill for that. Flood fills are for when you are not in control of what is being drawn, e.g a bitmap image provided by your user, or too complex shapes made by your user. In your case you really don't need this... – Kaiido Oct 07 '21 at 23:19
  • Thank you for letting me know and the solution below, I will take a look at it. – Allen Ye Oct 07 '21 at 23:22

1 Answers1

1

You can simply call fill() with the "evenodd" filling-rule.

However, this will cover the inner-half of your current stroke, as to avoid that, you can use compositing to draw behind the current drawings:

const canvas = document.getElementById('myCanvas');
const context = canvas.getContext('2d');
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const radius = 70;

context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);

context.rect(centerX-25, centerY-25, 50, 50)
context.lineWidth = 5;
context.strokeStyle = '#003300';
context.stroke();
context.fillStyle = "green";

canvas.onclick = ({ clientX, clientY }) => {
  const { left, top } = canvas.getBoundingClientRect();
  // if we're in the circle but not the inner rect
  if( context.isPointInPath( clientX - left, clientY - top, "evenodd" ) ) {
    // draw behind
    context.globalCompositeOperation = "destination-over";
    context.fill("evenodd");
    // do it only once
    canvas.onclick = null;
  }
};
<canvas width="300" height="300" id="myCanvas" style="border:1px solid #000000;"></canvas>
Kaiido
  • 123,334
  • 13
  • 219
  • 285