Toby Mellor
Toby Mellor

Reputation: 8205

Canvas - Is it possible fill over only a specific element on a canvas?

I have a main circle in the center. There are circles that are outside this that are always just touching the main circle. Outside circles can overlap each other.

Outside circles can have any line width, and for outside circles that have a smaller line width than the center circle, I'm trying to "eat into" the main circle.

If there were no overlapping circles, I could just set the outside circles to fill, which will eat in at the main circle. However, outside circles can overlap and filling them may break other outside circles.

Here's a picture to explain better: enter image description here

Here's a snippet showing the above example:

var canvas = document.getElementById("my-canvas"),
    ctx    = canvas.getContext("2d");
    
canvas.width  = $(window).width();
canvas.height = $(window).height();

drawCircle(174, 174, 100, 20);
drawCircle(300, 300, 75, 15);
drawCircle(400, 400, 66, 5);
drawCircle(325, 423, 50, 7);

function drawCircle(circleCenterX, circleCenterY, circleRadius, lineWidth) {
    ctx.beginPath();

    ctx.lineWidth = lineWidth;
    //canvasContext.fillStyle = "#22313f";
    ctx.arc(circleCenterX, circleCenterY, circleRadius, getRadiansFromPercentage(0), getRadiansFromPercentage(1)); // centerX, centerY, radius, startRadians, endRadians

    ctx.stroke();
}

function getRadiansFromPercentage(percentage) {
  var radians = (percentage * 360) * (Math.PI / 180); // turn percentage to degrees, then to radians

  return radians - 0.5 * Math.PI; // - 0.5 * Math.PI so "0.00" starts at the top of the circle
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="my-canvas"></canvas>

The end goal is for my project to look something like this:

enter image description here

Upvotes: 0

Views: 149

Answers (1)

Blindman67
Blindman67

Reputation: 54026

To do this you first draw the center circle as normal, then draw all the outside circles filled using ctx.globalCompositeOperation = "destination-out" this will remove pixels from the center circle. When all the outside circles rendered then set ctx.globalCompositeOperation = "source-over" and render the outside circles as normal.

Example code with random circles. I think this is what you are after.

canvas.width = 512;
canvas.height = 512;
const ctx = canvas.getContext("2d");

const circle = {
   x : 256,
   y : 256,
   r : 150,
   w : 10,
}

const circles = [];
function randCircle(){
  const ang = Math.random() * Math.PI * 2;
  const r = Math.random() * 100 + 50;
  const w = Math.random() * 18 + 2;
  const dist = circle.r - circle.w/2 + (r - w/2 );
  circles.push({
    x : circle.x + Math.cos(ang) * dist,
    y : circle.y + Math.sin(ang) * dist,
    r,
    w,
  })
}
function doit(){
  ctx.clearRect(0,0,512,512);
  circles.length = 0;
  /// some random circles
  for(let i = 0; i < 8; i ++){
    randCircle();
  }


  function renderAll(){
     ctx.lineWidth = circle.w;
     ctx.beginPath();
     ctx.arc(circle.x, circle.y, circle.r, 0 , Math.PI * 2);
     ctx.stroke();
     ctx.globalCompositeOperation = "destination-out";
     ctx.beginPath();
     for(var i = 0; i < circles.length; i ++){
       var c = circles[i];
       ctx.moveTo(c.x + c.r,c.y);
       ctx.arc(c.x,c.y,c.r,0,Math.PI * 2);
     }
     ctx.fill();
     ctx.globalCompositeOperation = "source-over";
     for(var i = 0; i < circles.length; i ++){
       ctx.beginPath();
       var c = circles[i];
       ctx.lineWidth = c.w;
       ctx.arc(c.x,c.y,c.r,0,Math.PI * 2);
       ctx.stroke();
     }

  }

  renderAll();
}

canvas.onclick = doit;
doit();
canvas {
 border : 2px solid black;
}
Click canvas to randomise.<br>
<canvas id="canvas"></canvas>

Upvotes: 1

Related Questions