堆叠圆圈会在边界半径处产生黑色条纹

10

我在这里遇到了一个相当棘手的企业。

我正在构建一只用作“手电筒/探照灯”的鼠标。如果发生悬停事件,所有文本(内联元素、按钮等等)都会从通常的白色反转为黑色,正常的背景是黄色调。

我目前拥有以下设置:

const _$shadow = $('.b-cursor__shadow');
const _$front = $('.b-cursor__front');
const _$back = $('.b-cursor__back');

$(document).on('mousemove', (e) => {
  _$back.css({
    left: e.pageX,
    top: e.pageY
  });
  _$front.css({
    left: e.pageX,
    top: e.pageY
  });
  _$shadow.css({
    left: e.pageX,
    top: e.pageY
  });
});
html,
body {
  padding: 0;
  margin: 0;
  cursor: none;
  background: red;
}

.test {
  background: darkblue;
}

p {
  color: white;
  font-family: sans-serif;
  font-size: 20px;
  max-width: 30rem;
  padding: 1rem;
  margin: 1rem;
  border: 1px solid white;
}

p,
span,
a {
  position: relative;
  z-index: 105;
}

.b-cursor__back,
.b-cursor__front,
.b-cursor__shadow {
  position: fixed;
  width: 8rem;
  height: 8rem;
  margin-left: -4rem;
  margin-top: -4rem;
  border-radius: 50%;
}

.b-cursor__shadow {
  box-shadow: 0px 0px 10px 10px rgba(231, 232, 192, 1);
}

/* background changes */
.b-cursor__back {
  z-index: 104;
  background: #18173e;
  clip-path: circle(50% at 50% 50%);
}

.b-cursor__front {
  z-index: 106;
  background: white;
  clip-path: circle(50% at 50% 50%);
  mix-blend-mode: difference;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis pretium pharetra ipsum, at placerat ante maximus vitae. Duis lacus urna, posuere id dapibus in, semper vitae massa. Quisque at egestas nisl. In ex elit, imperdiet eu interdum a, auctor vitae ante. Pellentesque efficitur imperdiet elementum. Integer at nibh gravida nisl sodales ornare ut quis est. Suspendisse sem odio, congue vitae felis at, tincidunt interdum purus. Morbi vitae efficitur est, non congue ante. Proin vel odio et metus sodales lobortis quis ut justo. Phasellus rhoncus eu urna vitae tristique. Suspendisse potenti. Curabitur quis quam lobortis mi laoreet lacinia. Cras non ultrices eros. Nam sed leo et tortor vestibulum cursus nec eu massa. Suspendisse potenti.</p>

<section class="b-cursor">
  <div class="b-cursor__shadow"></div>
  <div class="b-cursor__back"></div>
  <div class="b-cursor__front"></div>
</section>
<div class="test">
  <p>ja uh misschien werkt dit wel niet
    <p>
</div>

(codepen)

这几乎产生了预期的结果,除了border-radius: 50%不能正确处理半透明堆叠的div。像素问题!以下是澄清的图像:

Screenshot

问题:如何在保留文本当前效果的同时移除由两个相同大小元素堆叠创建的黑色边框?


1
我认为你可以更容易地使用单个带有径向渐变的SVG作为光标,然后在SVG上应用mix-blend-mode将其与背景差异合成... - AKX
2
与您的问题无关,但滚动确实会使聚光灯定位失准。 - Turnip
@AKX 嘿AKX,我试图在单个SVG中应用该效果,但我使用z-index堆栈使内容出现在元素“之间”,因此差异效果在白色文本上起作用。 - roberrrt-s
1
看起来与这个问题有关,但不是重复的。 - Christian Vincenzo Traina
1
https://codepen.io/anon/pen/OdwoWY 可能只需使用内嵌的盒子阴影(我知道这不完全是相同的结果,但没有黑色条)。 - Superdrac
显示剩余3条评论
3个回答

9

我会使用伪元素添加另一个元素来隐藏那个小边框,并通过移动容器而不是每个元素来简化代码。此外,不需要使用clip-path

const _$cursor = $('.b-cursor');

$(document).on('mousemove', (e) => {
  _$cursor.css({
    left: e.pageX,
    top: e.pageY
  });
});
html,
body {
  padding: 0;
  margin: 0;
  cursor: none;
  background: red;
}

.test {
  background: darkblue;
}

p {
  color: white;
  font-family: sans-serif;
  font-size: 20px;
  max-width: 30rem;
  padding: 1rem;
  margin: 1rem;
  border: 1px solid white;
}

p,
span,
a {
  position: relative;
  z-index: 105;
}

.b-cursor { /*no z-index here !!!*/
  position: absolute;
  width: 8rem;
  height: 8rem;
  margin-left: -4rem;
  margin-top: -4rem;
}
/*the magic element*/
.b-cursor:before {
  content:"";
  position:absolute;
  top:-1px;
  left:-1px;
  right:-1px;
  bottom:-1px;
  border:2px solid rgba(231, 232, 192, 1);
  border-radius:50%;
  z-index:999;
}
/**/

.b-cursor__back,
.b-cursor__front,
.b-cursor__shadow {
  position:absolute;
  top:0;
  left:0;
  width:100%;
  height:100%;
  border-radius: 50%;
}

.b-cursor__shadow {
  box-shadow: 0px 0px 10px 10px rgba(231, 232, 192, 1);
}

/* background changes */
.b-cursor__back {
  z-index: 104;
  background: #18173e;
}

.b-cursor__front {
  z-index: 106;
  background: white;
  mix-blend-mode: difference;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis pretium pharetra ipsum, at placerat ante maximus vitae. Duis lacus urna, posuere id dapibus in, semper vitae massa. Quisque at egestas nisl. In ex elit, imperdiet eu interdum a, auctor vitae ante. Pellentesque efficitur imperdiet elementum. Integer at nibh gravida nisl sodales ornare ut quis est. Suspendisse sem odio, congue vitae felis at, tincidunt interdum purus. Morbi vitae efficitur est, non congue ante. Proin vel odio et metus sodales lobortis quis ut justo. Phasellus rhoncus eu urna vitae tristique. Suspendisse potenti. Curabitur quis quam lobortis mi laoreet lacinia. Cras non ultrices eros. Nam sed leo et tortor vestibulum cursus nec eu massa. Suspendisse potenti.</p>

<section class="b-cursor">
  <div class="b-cursor__back"></div>
  <div class="b-cursor__front"></div>
  <div class="b-cursor__shadow"></div>
</section>
<div class="test">
  <p>ja uh misschien werkt dit wel niet</p>
</div>

这里有另一个更少代码的想法,使用纯JS而不是jQuery:

document.onmousemove = function(e) {
  document.body.style.setProperty('--mx',(e.pageX)+'px');
  document.body.style.setProperty('--my',(e.pageY)+'px');
  
  document.body.style.setProperty('--x',(e.clientX)+'px');
  document.body.style.setProperty('--y',(e.clientY)+'px');
  

}
html {
  background:red;
}
body{
  padding: 1px;
  margin: 0;
  min-height:100vh;
  cursor: none;
  background:
    radial-gradient(circle at var(--x) var(--y),#18173e 4rem,transparent 4rem) fixed;
}

.test {
  background:
    radial-gradient(circle at var(--x) var(--y),#18173e 4rem,transparent 4rem) fixed,
    darkblue;
}

p {
  color: white;
  font-family: sans-serif;
  font-size: 20px;
  max-width: 30rem;
  padding: 1rem;
  margin: 1rem;
  border: 1px solid white;
}

.b-cursor { /*no z-index here !!!*/
  position: absolute;
  width: 8rem;
  height: 8rem;
  top:var(--my);
  left:var(--mx);
  margin-left: -4rem;
  margin-top: -4rem;
}
/*the magic element*/
.b-cursor:before{
  content:"";
  position:absolute;
  top:-1px;
  left:-1px;
  right:-1px;
  bottom:-1px;
  border:2px solid rgba(231, 232, 192, 1);
  border-radius:50%;
  z-index:999;
}
.b-cursor:after {
  content:"";
  position:absolute;
  top:0;
  left:0;
  right:0;
  bottom:0;
  box-shadow: 0px 0px 10px 10px rgba(231, 232, 192, 1);
  border-radius:50%;
  z-index:998;
}
/**/
.b-cursor > div {
  position:absolute;
  top:0;
  left:0;
  width:100%;
  height:100%;
  border-radius: 50%;
  z-index: 997;
  background: white;
  mix-blend-mode: difference;
}
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis pretium pharetra ipsum, at placerat ante maximus vitae. Duis lacus urna, posuere id dapibus in, semper vitae massa. Quisque at egestas nisl. In ex elit, imperdiet eu interdum a, auctor vitae ante. Pellentesque efficitur imperdiet elementum. Integer at nibh gravida nisl sodales ornare ut quis est. Suspendisse sem odio, congue vitae felis at, tincidunt interdum purus. Morbi vitae efficitur est, non congue ante. Proin vel odio et metus sodales lobortis quis ut justo. Phasellus rhoncus eu urna vitae tristique. Suspendisse potenti. Curabitur quis quam lobortis mi laoreet lacinia. Cras non ultrices eros. Nam sed leo et tortor vestibulum cursus nec eu massa. Suspendisse potenti.</p>

<section class="b-cursor">
  <div></div>
</section>
<div class="test">
  <p>ja uh misschien werkt dit wel niet</p>
</div>

如果我们考虑使用另一个渐变来替换我们用于修复黑色边框的阴影和边框,则仍然可以进行更多优化:

document.onmousemove = function(e) {
  document.body.style.setProperty('--mx',(e.pageX)+'px');
  document.body.style.setProperty('--my',(e.pageY)+'px');
  
  document.body.style.setProperty('--x',(e.clientX)+'px');
  document.body.style.setProperty('--y',(e.clientY)+'px');
  

}
html {
  background:red;
}
body{
  padding: 1px;
  margin: 0;
  min-height:100vh;
  cursor: none;
  background:
    radial-gradient(circle at var(--x) var(--y),#18173e 4rem,transparent 4rem) fixed;
}

.test {
  background:
    radial-gradient(circle at var(--x) var(--y),#18173e 4rem,transparent 4rem) fixed,
    darkblue;
}

p {
  color: white;
  font-family: sans-serif;
  font-size: 20px;
  max-width: 30rem;
  padding: 1rem;
  margin: 1rem;
  border: 1px solid white;
}

.b-cursor { /*no z-index here !!!*/
  position: absolute;
  width: 8rem;
  height: 8rem;
  top:var(--my);
  left:var(--mx);
  margin-left: -4rem;
  margin-top: -4rem;
}

.b-cursor:before{
  content:"";
  position:absolute;
  top:-10px;
  left:-10px;
  right:-10px;
  bottom:-10px;
  background:
    radial-gradient(farthest-side, 
      transparent calc(100% - 18px),rgba(231, 232, 192, 1) calc(100% - 15px),
      rgba(231, 232, 192, 1) calc(100% - 10px),transparent 100%);
  border-radius:50%;
  z-index:999;
}

.b-cursor:after {
  content:"";
  position:absolute;
  top:0;
  left:0;
  width:100%;
  height:100%;
  border-radius: 50%;
  z-index: 998;
  background: white;
  mix-blend-mode: difference;
}
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis pretium pharetra ipsum, at placerat ante maximus vitae. Duis lacus urna, posuere id dapibus in, semper vitae massa. Quisque at egestas nisl. In ex elit, imperdiet eu interdum a, auctor vitae ante. Pellentesque efficitur imperdiet elementum. Integer at nibh gravida nisl sodales ornare ut quis est. Suspendisse sem odio, congue vitae felis at, tincidunt interdum purus. Morbi vitae efficitur est, non congue ante. Proin vel odio et metus sodales lobortis quis ut justo. Phasellus rhoncus eu urna vitae tristique. Suspendisse potenti. Curabitur quis quam lobortis mi laoreet lacinia. Cras non ultrices eros. Nam sed leo et tortor vestibulum cursus nec eu massa. Suspendisse potenti.</p>

<section class="b-cursor">
</section>
<div class="test">
  <p>ja uh misschien werkt dit wel niet</p>
</div>


Safari不支持使用at的渐变语法,如此问题所述,因此这里提供另一种选择:

document.onmousemove = function(e) {
  document.body.style.setProperty('--mx',(e.pageX)+'px');
  document.body.style.setProperty('--my',(e.pageY)+'px');
  
  document.body.style.setProperty('--x',(e.clientX)+'px');
  document.body.style.setProperty('--y',(e.clientY)+'px');
  

}
html {
  background:red;
}
body{
  padding: 1px;
  margin: 0;
  min-height:100vh;
  cursor: none;
  background:
    radial-gradient(farthest-side ,#18173e 100%,transparent 100%)
    calc(var(--x) - 4rem) calc(var(--y) - 4rem)/8rem 8rem  fixed no-repeat;
}

.test {
  background:
    radial-gradient(farthest-side ,#18173e 100%,transparent 100%)
    calc(var(--x) - 4rem) calc(var(--y) - 4rem)/8rem 8rem  fixed no-repeat,
    darkblue;
}

p {
  color: white;
  font-family: sans-serif;
  font-size: 20px;
  max-width: 30rem;
  padding: 1rem;
  margin: 1rem;
  border: 1px solid white;
}

.b-cursor { /*no z-index here !!!*/
  position: absolute;
  width: 8rem;
  height: 8rem;
  top:var(--my);
  left:var(--mx);
  margin-left: -4rem;
  margin-top: -4rem;
}

.b-cursor:before{
  content:"";
  position:absolute;
  top:-10px;
  left:-10px;
  right:-10px;
  bottom:-10px;
  background:
    radial-gradient(farthest-side, 
      transparent calc(100% - 18px),rgba(231, 232, 192, 1) calc(100% - 15px),
      rgba(231, 232, 192, 1) calc(100% - 10px),transparent 100%);
  border-radius:50%;
  z-index:999;
}

.b-cursor:after {
  content:"";
  position:absolute;
  top:0;
  left:0;
  width:100%;
  height:100%;
  border-radius: 50%;
  z-index: 998;
  background: white;
  mix-blend-mode: difference;
}
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis pretium pharetra ipsum, at placerat ante maximus vitae. Duis lacus urna, posuere id dapibus in, semper vitae massa. Quisque at egestas nisl. In ex elit, imperdiet eu interdum a, auctor vitae ante. Pellentesque efficitur imperdiet elementum. Integer at nibh gravida nisl sodales ornare ut quis est. Suspendisse sem odio, congue vitae felis at, tincidunt interdum purus. Morbi vitae efficitur est, non congue ante. Proin vel odio et metus sodales lobortis quis ut justo. Phasellus rhoncus eu urna vitae tristique. Suspendisse potenti. Curabitur quis quam lobortis mi laoreet lacinia. Cras non ultrices eros. Nam sed leo et tortor vestibulum cursus nec eu massa. Suspendisse potenti.</p>

<section class="b-cursor">
</section>
<div class="test">
  <p>ja uh misschien werkt dit wel niet</p>
</div>


4
哇,这太酷了——我在Meta帖子上发现的。如果你在不移动鼠标的情况下滚动滚轮,你会看到一个蓝色的圆圈,反转效果将停留在原地,但只要你移动鼠标一像素,它们就会重新合并。 - Patrick Artner

4
这可能符合您的需求。

const _$shadow = $('.b-cursor__shadow');
const _$front = $('.b-cursor__front');
const _$back = $('.b-cursor__back');

$(document).on('mousemove', (e) => {
  _$back.css({
    left: e.pageX,
    top: e.pageY
  });
  _$front.css({
    left: e.pageX,
    top: e.pageY
  });
  _$shadow.css({
    left: e.pageX,
    top: e.pageY
  });
});
html, body {
    padding: 0;
    margin: 0;
    cursor: none;
    background: red;
}
.test {
    background: darkblue;
}
p {
    color: white;
    font-family: sans-serif;
    font-size: 20px;
    max-width: 30rem;
    padding: 1rem;
    margin: 1rem;
    border: 1px solid white;
}
p, span, a {
    position: relative;
    z-index: 105;
}
.b-cursor__shadow2, .b-cursor__back, .b-cursor__front, .b-cursor__shadow {
    position: fixed;
    width: 8rem;
    height: 8rem;
    margin-left: -4rem;
    margin-top: -4rem;
    border-radius: 50%;
}
.b-cursor__shadow {
    box-shadow: 0px 0px 10px 20px rgba(231, 232, 192, 1);
    z-index: 107;
    height: 8rem;
    width: 8rem;
}
.b-cursor__shadow2 {
    background: radial-gradient(circle at center, #18173e 100%, #18173e 25%);
    z-index: 109;
    height: 8rem;
    width: 8rem;
    background-color: transparent;
}
/* background changes */
.b-cursor__back {
    z-index: 104;
    height: 8rem;
    width: 8rem;
    background: radial-gradient(circle at center, #18173e 100%, #18173e 25%);
    background-size: 100% 100%;
    background-position: 50% 50%;
}
.b-cursor__back:after {
    width: 7rem;
    height: 7rem;
    content: '';
    border-radius: 50%;
    background: transparent;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    box-shadow: 0px 0px 0px 1rem #18173e;
    transition: all 0.3s linear;
    mix-blend-mode: normal;
}
.b-cursor__front {
    z-index: 106;
    height: 8rem;
    width: 8rem;
    background: white;
    background: radial-gradient(circle at center, #ffffff 100%, #ffffff 25%);
    background-position: 50% 50%;
    mix-blend-mode: difference;
}
.b-cursor__front:after {
    width: 7rem;
    height: 7rem;
    content: '';
    border-radius: 50%;
    background: transparent;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    box-shadow: 0px 0px 0px 1rem #ffffff;
    transition: all 0.3s linear;
    mix-blend-mode: normal;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis pretium pharetra ipsum, at placerat ante maximus vitae. Duis lacus urna, posuere id dapibus in, semper vitae massa. Quisque at egestas nisl. In ex elit, imperdiet eu interdum a, auctor vitae
  ante. Pellentesque efficitur imperdiet elementum. Integer at nibh gravida nisl sodales ornare ut quis est. Suspendisse sem odio, congue vitae felis at, tincidunt interdum purus. Morbi vitae efficitur est, non congue ante. Proin vel odio et metus sodales
  lobortis quis ut justo. Phasellus rhoncus eu urna vitae tristique. Suspendisse potenti. Curabitur quis quam lobortis mi laoreet lacinia. Cras non ultrices eros. Nam sed leo et tortor vestibulum cursus nec eu massa. Suspendisse potenti.</p>

<section class="b-cursor">
  <div class="b-cursor__shadow"></div>
  <div class="b-cursor__back"></div>
  <div class="b-cursor__front"></div>
  <div class="cursor_now"></div>
</section>
<div class="test">
  <p>ja uh misschien werkt dit wel niet
    <p>
</div>


0
尝试在CSS中将以下代码:

filter:blur(1.4px); /* 或者任何介于0.7px至1.9px之间的值 */

添加到外圆或内圆中。

嗨,Ryan,这是不可能的,因为它会改变由z-index创建的堆叠上下文。 - roberrrt-s
我认为SVG建议是最好的选择,因为它们不是像素相关的,并且可能解决您的问题。 - Ryan Stone

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