重叠圆形区域的填充

10
我有两个相交的圆,我想让它们相交的区域有一个颜色,即使这两个圆是透明的。我想我可以通过CSS的mix-blend-mode属性找到某种方法来实现这一点,但我在使用它时没有成功。
当然,我可以让圆有颜色并降低它们的不透明度,但我希望它们要么是白色的或透明的,只有重叠的区域得到背景颜色。
我希望相交的区域能够动态地改变,因为一个圆将跟随鼠标移动。这里是那个 CodePen
我不确定从哪里开始解决这个问题,如果CSS有一些技巧,还是必须使用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>


这必须仅在SVG中可行。 - Mister Jojo
有趣的问题,我认为你可以以某种方式利用它。我将尝试自己实现。https://stackoverflow.com/questions/54008991/placing-divs-around-a-circle-with-starting-and-ending-angles - zod
这是关于编程的内容,以下为翻译文本:此外,这里提供一个链接:https://stackoverflow.com/questions/25066171/responsive-transparent-overlapping-css-circles-with-text-align-vertical-and-cent,可能会有所帮助。 - Nemer
1
@Haroldo_OK 看起来很有前途,我会研究一下的,谢谢。 - Souleste
1
我建议不要自己编写代码,而是使用一些内置布尔形状操作的工具,例如paperjs,在那里您可以定义两个圆,然后只需请求shape = c1.union(c2)并以任何您想要的颜色进行着色。 - Mike 'Pomax' Kamermans
显示剩余3条评论
3个回答

4

您可以通过在主圆圈内添加一个“内部光标”圆圈来处理。基于鼠标移动,它将随主光标移动,从而给人们以重叠的错觉。

在这种情况下,交汇圆形的背景颜色并不重要。此外,您不必担心混合模式,因为内部光标具有背景颜色并且被隐藏。只有当鼠标悬停在主圆圈上时才会显示它。

请参考以下示例:

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


太棒了。我一直在过度思考,认为我需要计算“重叠”元素的形状和曲率。谢谢!我会保持悬赏开放,这样我就可以看到不同的解决方法。 - Souleste
@Souleste,您将如何处理被剪切路径隐藏的小圆圈? - Temani Afif

3

一个只使用 CSS 的简单方法是考虑使用 background-attachement:fixed,将 radial-gradient 作为背景。您可以将此背景应用于光标元素,并使其尺寸/位置与固定元素相同。

您需要在代码中添加的所有内容如下:

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

我还优化了代码,删除了使用伪元素绘制的线条,考虑了 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>

如果圆圈不在中间,您只需调整位置即可。

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

我们基本上使用相同的top/left值来实现以下效果:
radial-gradient(circle at [left] [top],blue [radius],transparent [radius]);

我在透明度上加了1像素以避免出现锯齿状的边缘


2
如果您熟悉HTML的<canvas>元素,那么它可能是这种任务的最佳选择,我怀疑有CSS方法可以实现您想要的效果。
(如果有CSS方法,它可能比使用画布更复杂,性能也更慢)
您可以查看下面的代码作为灵感来源(源代码和实例)。

<!DOCTYPE HTML>
    <html>
      <head>
        <style>
          body {
            margin: 0px;
            padding: 0px;
          }
        </style>
      </head>
      <body data-rsssl=1>
        <canvas id="myCanvas" width="578" height="200"></canvas>
        <script>
          var canvas = document.getElementById('myCanvas');
          var context = canvas.getContext('2d');
          var x = canvas.width / 2;
          var y = canvas.height / 2;
          var radius = 75;
          var offset = 50;
    
          /*
           * save() allows us to save the canvas context before
           * defining the clipping region so that we can return
           * to the default state later on
           */
          context.save();
          context.beginPath();
          context.arc(x, y, radius, 0, 2 * Math.PI, false);
          context.clip();
    
          // draw blue circle inside clipping region
          context.beginPath();
          context.arc(x - offset, y - offset, radius, 0, 2 * Math.PI, false);
          context.fillStyle = 'blue';
          context.fill();
    
          // draw yellow circle inside clipping region
          context.beginPath();
          context.arc(x + offset, y, radius, 0, 2 * Math.PI, false);
          context.fillStyle = 'yellow';
          context.fill();
    
          // draw red circle inside clipping region
          context.beginPath();
          context.arc(x, y + offset, radius, 0, 2 * Math.PI, false);
          context.fillStyle = 'red';
          context.fill();
    
          /*
           * restore() restores the canvas context to its original state
           * before we defined the clipping region
           */
          context.restore();
          context.beginPath();
          context.arc(x, y, radius, 0, 2 * Math.PI, false);
          context.lineWidth = 10;
          context.strokeStyle = 'blue';
          context.stroke();
        </script>
      </body>
    </html>      


我想这是我可以研究的问题,但问题在于圆形交叉点可能会一直变化,因为我正在使用它作为可拖动圆形,无论用户将圆形移动到哪里,都会显示交叉点。 - Souleste
clip() 无法处理抗锯齿。对于圆形,你应该避免使用它。最好使用合成代替。 - Kaiido

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接