【问题标题】:Fill in overlapping circle area填充重叠的圆形区域
【发布时间】:2020-05-19 05:21:45
【问题描述】:

我有两个相交的圆,我想让相交的区域有颜色,即使这两个圆是透明的。我以为我可以找到一些方法来使用 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中可行
  • 有趣的问题,我想你以某种方式利用了这个。我要试试自己。stackoverflow.com/questions/54008991/…
  • 这里的链接也可以提供帮助:stackoverflow.com/questions/25066171/…
  • @Haroldo_OK 看起来很有希望,我会研究一下,谢谢。
  • 我建议不要滚动你自己的代码,而是使用像 paperjs 这样的带有布尔形状操作的东西,在其中定义你的两个圆圈,然后只要求 shape = c1.union(c2) 和颜色但是你想要的。

标签: javascript jquery html css


【解决方案1】:

解决此问题的一种方法是在主圆圈内添加一个“内部光标”圆圈。考虑到重叠的错觉,它会根据鼠标移动与主光标一起移动。

在这种情况下,相交圆圈的背景颜色无关紧要。此外,您不必担心混合混合模式,因为内部光标具有背景颜色并且是隐藏的。仅当鼠标悬停在主圆圈上时才能查看。

看这个例子:

$(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 你将如何管理被剪辑路径隐藏的小圆圈?
【解决方案2】:

仅使用 CSS 的简单想法是使用 background-attachement:fixedradial-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]);

我将1px添加到透明以避免锯齿状边缘

【讨论】:

    【解决方案3】:

    如果您对 HTML &lt;canvas&gt; 元素感到满意,它可能是此类任务的最佳朋友,我怀疑是否有 CSS 方法可以实现您想要的。

    如果有 CSS 方式,它可能会比使用画布更复杂,性能也更慢

    你可以看看下面的代码以获得灵感(source and live example

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

    【讨论】:

    • 猜猜这是我可以查看的东西,问题是,圆的交点可能总是在变化,因为我使用它的是一个可拖动的圆,它在用户移动圆的任何地方显示交点到。
    • clip() 无法处理抗锯齿。对于圆形,你真的应该避免它。更喜欢compositing
    猜你喜欢
    • 2012-02-02
    • 1970-01-01
    • 2012-03-30
    • 1970-01-01
    • 2013-02-02
    • 2022-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多