Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fill in overlapping circle area

I have two circles that intersect and I want to make the intersecting area have a color, even when the two circles are transparent. I thought I could find some way to do this with css mix-blend-mode property but I have had no success with it.

Of course, I could make the circles have color and decrease their opacity, but I want them to be either white or transparent, where only the overlapped area gets background color.

I want the intersecting area to be able to change dynamically because one circle will follow the mouse. Here is the codepen for that.

I'm not sure where to start on this, if css has some technique or if it will have to be done with jquery.

$(document).mousemove(function(e) { 

  $('.cursor').eq(0).css({
    left: e.pageX - 25,
    top: e.pageY - 20
  });

  // circles
  var c1 = $('.cursor');
  var c2 = $('.circle');
  
  // radius
  var d1 = c1.outerWidth(true)/2;
  var d2 = c2.outerWidth(true)/2;
  
  // centers of first circle
  var x1 = c1.offset().left + c1.width()/2;  
  var y1 = c1.offset().top + c1.height()/2;
  
  // centers of second circle
  var x2 = c2.offset().left + c2.width()/2;
  var y2 = c2.offset().top + c2.height()/2;
  
  var i1 = c2.find('.inter1');
  var i2 = c2.find('.inter2');
  var o = c1.find('.overlap');
  
  function calc() {
    var a = d2;
    var b = d1;
    var c = Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
    var d = (b*b+c*c-a*a)/(2*c);
    var h = Math.sqrt((b*b) - (d*d));
    if (d < 0 || $.isNumeric(h)) {
      c2.css('border-color', 'red');
    } else {
      c2.css('border-color', 'black');
    }
    var x3 = (x2-x1)*d/c + (y2-y1)*h/c +  x1;
    var y3 = (y2-y1)*d/c - (x2-x1)*h/c +  y1;
    var x4 = (x2-x1)*d/c - (y2-y1)*h/c +  x1;
    var y4 = (y2-y1)*d/c + (x2-x1)*h/c +  y1;
    
    if ($.isNumeric(h)) {
      i1.show();
      i2.show();
    } else {
      i1.hide();
      i2.hide();
    }
    i1.offset({ top: y3-5, left: x3-5});
    i2.offset({ top: y4-5, left: x4-5});
  } calc();
  
  
});
body {
  background: #fff;
}

.overlap {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background: rgba(0, 0, 0, 0.5);
}

.cursor {
  height: 50px;
  width: 50px;
  border-radius: 50%;
  position: absolute;
  pointer-events: none;
  z-index: 999;
  border: 1px solid black;
  outline: 1px solid #c9d3ff;
  overflow: none;
}

.circle {
  border-radius: 50%;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 200px;
  height: 200px;
  border: 1px solid black;
  outline: 1px solid #c9d3ff;
}

.circle::after,
.cursor::after {
  display: block;
  content: '';
  height: 1px;
  background: #c9d3ff;
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
}

.circle::before,
.cursor::before {
  display: block;
  content: '';
  width: 1px;
  background: #c9d3ff;
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
}

.inter {
  width: 10px;
  height: 10px;
  background: black;
  border-radius: 50%;
  position: absolute;
  display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="cursor">
</div>

<div class="circle">
  <div class="inter1 inter"></div>
  <div class="inter2 inter"></div>
  <div>
like image 990
Souleste Avatar asked Aug 26 '19 21:08

Souleste


People also ask

What is the overlapping area of two circles called?

What Is a Venn Diagram? A Venn diagram is an illustration that uses circles to show the relationships among things or finite groups of things. Circles that overlap have a commonality while circles that do not overlap do not share those traits.

What are overlapping circles?

An overlapping circles grid is a geometric pattern of repeating, overlapping circles of an equal radius in two-dimensional space. Commonly, designs are based on circles centered on triangles (with the simple, two circle form named vesica piscis) or on the square lattice pattern of points.


2 Answers

One way you can approach this is by adding a "inner-cursor" circle inside the main circle. Based on mouse movement it will move with the main cursor given the illusion of overlap.

In this case, the background color of the intersecting circles will not matter. Also, you do not have to worry about mix-blend-mode since the inner cursor has a background color and is hidden. It is only viewed if the mouse hovers over the main circle.

See this example:

$(document).mousemove(function(e) {

  // elements
  let cursor = $('.cursor');
  let innerCursor = $('.inner-cursor');
  let c2 = $('.circle');

  let pos = {
    left: e.pageX - 25,
    top: e.pageY - 20
  };
  cursor.css(pos);

  innerCursor.css({
    left: pos.left - c2.offset().left,
    top:  pos.top - c2.offset().top
  });

  // circles


  // radius
  var d1 = cursor.outerWidth(true) / 2;
  var d2 = c2.outerWidth(true) / 2;

  // centers of first circle
  var x1 = cursor.offset().left + cursor.width() / 2;
  var y1 = cursor.offset().top + cursor.height() / 2;

  // centers of second circle
  var x2 = c2.offset().left + c2.width() / 2;
  var y2 = c2.offset().top + c2.height() / 2;

  var i1 = c2.find('.inter1');
  var i2 = c2.find('.inter2');
  var o = cursor.find('.overlap');

  function calc() {
    var a = d2;
    var b = d1;
    var c = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
    var d = (b * b + c * c - a * a) / (2 * c);
    var h = Math.sqrt((b * b) - (d * d));
    // console.log(a, b, c, d, h);
    if (d < 0 || $.isNumeric(h)) {
      c2.css('border-color', 'red');
    } else {
      c2.css('border-color', 'black');
    }
    var x3 = (x2 - x1) * d / c + (y2 - y1) * h / c + x1;
    var y3 = (y2 - y1) * d / c - (x2 - x1) * h / c + y1;
    var x4 = (x2 - x1) * d / c - (y2 - y1) * h / c + x1;
    var y4 = (y2 - y1) * d / c + (x2 - x1) * h / c + y1;

    if ($.isNumeric(h)) {
      i1.show();
      i2.show();
    } else {
      i1.hide();
      i2.hide();
    }
    i1.offset({
      top: y3 - 5,
      left: x3 - 5
    });
    i2.offset({
      top: y4 - 5,
      left: x4 - 5
    });
  }
  calc();

});
body {
  background: #fff;
}

.clip {
  display: inline-block;
  background: blue;
  height: 50px;
  width: 50px;
  border-radius: 50%;
  clip-path: ellipse(50px 50px at 50% 0%);
  position: absolute;
  left: 750px;
  top: 40px;
}

.cursor {
  left: 750px;
  top: 40px;
}

.cursor {
  height: 50px;
  width: 50px;
  border-radius: 50%;
  position: absolute;
  pointer-events: none;
  z-index: 999;
  border: 1px solid black;
  outline: 1px solid #c9d3ff;
  overflow: none;
  mix-blend-mode: multiply;
  background: rgba(100, 100, 100, 0.1);
}

.circle {
  background: rgba(100, 100, 100, 0.1);
  border-radius: 50%;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 200px;
  height: 200px;
  border: 1px solid black;
  outline: 1px solid #c9d3ff;
  overflow: hidden;
}

.circle::after,
.cursor::after {
  display: block;
  content: '';
  height: 1px;
  background: #c9d3ff;
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
}

.circle::before,
.cursor::before {
  display: block;
  content: '';
  width: 1px;
  background: #c9d3ff;
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
}

.inter {
  width: 10px;
  height: 10px;
  background: black;
  border-radius: 50%;
  position: absolute;
  display: none;
}

.inner-cursor {
  height: 50px;
  width: 50px;
  border-radius: 50%;
  position: absolute;
  pointer-events: none;
  background: green;
  left: 50%;
  top: 50%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="cursor">

</div>
<span class="clip"></span>

<div class="circle">
  <div class='inner-cursor'></div>
  <div class="inter1 inter"></div>
  <div class="inter2 inter"></div>
</div>
like image 159
Kalimah Avatar answered Oct 29 '22 21:10

Kalimah


On easy idea using only CSS is to consider a radial-gradient as background using background-attachement:fixed. You apply this background to the cursor element and you make its dimension/position the same as the fixed element.

All you need to add in your code is:

background:radial-gradient(circle,blue 100px,transparent 100px) fixed no-repeat

I have also optimized the code to remove the line you draw with pseudo element to consider linear-gradient

$(document).mousemove(function(e) { 

  $('.cursor').eq(0).css({
    left: e.pageX - 25,
    top: e.pageY - 20
  });

  // circles
  var c1 = $('.cursor');
  var c2 = $('.circle');
  
  // radius
  var d1 = c1.outerWidth(true)/2;
  var d2 = c2.outerWidth(true)/2;
  
  // centers of first circle
  var x1 = c1.offset().left + c1.width()/2;  
  var y1 = c1.offset().top + c1.height()/2;
  
  // centers of second circle
  var x2 = c2.offset().left + c2.width()/2;
  var y2 = c2.offset().top + c2.height()/2;
  
  var i1 = c2.find('.inter1');
  var i2 = c2.find('.inter2');
  var o = c1.find('.overlap');
  
  function calc() {
    var a = d2;
    var b = d1;
    var c = Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
    var d = (b*b+c*c-a*a)/(2*c);
    var h = Math.sqrt((b*b) - (d*d));
    if (d < 0 || $.isNumeric(h)) {
      c2.css('border-color', 'red');
    } else {
      c2.css('border-color', 'black');
    }
    var x3 = (x2-x1)*d/c + (y2-y1)*h/c +  x1;
    var y3 = (y2-y1)*d/c - (x2-x1)*h/c +  y1;
    var x4 = (x2-x1)*d/c - (y2-y1)*h/c +  x1;
    var y4 = (y2-y1)*d/c + (x2-x1)*h/c +  y1;
    
    if ($.isNumeric(h)) {
      i1.show();
      i2.show();
    } else {
      i1.hide();
      i2.hide();
    }
    i1.offset({ top: y3-5, left: x3-5});
    i2.offset({ top: y4-5, left: x4-5});
  } calc();
  
  
});
body {
  background: #fff;
  margin:0;
}
*{
  box-sizing:border-box;
}


.cursor {
  height: 50px;
  width: 50px;
  border-radius: 50%;
  position: absolute;
  pointer-events: none;
  z-index: 999;
  border: 1px solid black;
  outline: 1px solid #c9d3ff;
  background:
    linear-gradient(#c9d3ff,#c9d3ff) center/100% 1px,
    linear-gradient(#c9d3ff,#c9d3ff) center/1px 100%,
    
    radial-gradient(circle,blue 100px,transparent 101px) fixed,
    yellow;
  background-repeat:no-repeat;
}

.circle {
  border-radius: 50%;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 200px;
  height: 200px;
  border: 1px solid black;
  outline: 1px solid #c9d3ff;
  background:
    linear-gradient(#c9d3ff,#c9d3ff) center/100% 1px,
    linear-gradient(#c9d3ff,#c9d3ff) center/1px 100%,
    #f2f2f2;
  background-repeat:no-repeat;
}


.inter {
  width: 10px;
  height: 10px;
  background: black;
  border-radius: 50%;
  position: absolute;
  display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="cursor">
</div>

<div class="circle">
  <div class="inter1 inter"></div>
  <div class="inter2 inter"></div>
<div>

And if the circle is not in the middle you simply adjust the position.

$(document).mousemove(function(e) { 

  $('.cursor').eq(0).css({
    left: e.pageX - 25,
    top: e.pageY - 20
  });

  // circles
  var c1 = $('.cursor');
  var c2 = $('.circle');
  
  // radius
  var d1 = c1.outerWidth(true)/2;
  var d2 = c2.outerWidth(true)/2;
  
  // centers of first circle
  var x1 = c1.offset().left + c1.width()/2;  
  var y1 = c1.offset().top + c1.height()/2;
  
  // centers of second circle
  var x2 = c2.offset().left + c2.width()/2;
  var y2 = c2.offset().top + c2.height()/2;
  
  var i1 = c2.find('.inter1');
  var i2 = c2.find('.inter2');
  var o = c1.find('.overlap');
  
  function calc() {
    var a = d2;
    var b = d1;
    var c = Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
    var d = (b*b+c*c-a*a)/(2*c);
    var h = Math.sqrt((b*b) - (d*d));
    if (d < 0 || $.isNumeric(h)) {
      c2.css('border-color', 'red');
    } else {
      c2.css('border-color', 'black');
    }
    var x3 = (x2-x1)*d/c + (y2-y1)*h/c +  x1;
    var y3 = (y2-y1)*d/c - (x2-x1)*h/c +  y1;
    var x4 = (x2-x1)*d/c - (y2-y1)*h/c +  x1;
    var y4 = (y2-y1)*d/c + (x2-x1)*h/c +  y1;
    
    if ($.isNumeric(h)) {
      i1.show();
      i2.show();
    } else {
      i1.hide();
      i2.hide();
    }
    i1.offset({ top: y3-5, left: x3-5});
    i2.offset({ top: y4-5, left: x4-5});
  } calc();
  
  
});
body {
  background: #fff;
  margin:0;
}
*{
  box-sizing:border-box;
}



.cursor {
  height: 50px;
  width: 50px;
  border-radius: 50%;
  position: absolute;
  pointer-events: none;
  z-index: 999;
  border: 1px solid black;
  outline: 1px solid #c9d3ff;
  background:
    linear-gradient(#c9d3ff,#c9d3ff) center/100% 1px,
    linear-gradient(#c9d3ff,#c9d3ff) center/1px 100%,
    
    radial-gradient(circle at 20% 50%,blue 100px,transparent 101px) fixed
    yellow;
  background-repeat:no-repeat;
}

.circle {
  border-radius: 50%;
  position: absolute;
  top: 50%;
  left: 20%;
  transform: translate(-50%, -50%);
  width: 200px;
  height: 200px;
  border: 1px solid black;
  outline: 1px solid #c9d3ff;
  background:
    linear-gradient(#c9d3ff,#c9d3ff) center/100% 1px,
    linear-gradient(#c9d3ff,#c9d3ff) center/1px 100%,
    #f2f2f2;
  background-repeat:no-repeat;
}


.inter {
  width: 10px;
  height: 10px;
  background: black;
  border-radius: 50%;
  position: absolute;
  display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="cursor">
</div>

<div class="circle">
  <div class="inter1 inter"></div>
  <div class="inter2 inter"></div>
<div>

We basically use the same value of top/left to have the following:

radial-gradient(circle at [left] [top],blue [radius],transparent [radius]);

I add 1px to transparent to avoid the jagged edge

like image 33
Temani Afif Avatar answered Oct 29 '22 22:10

Temani Afif