How can I make my text visible when my custom curser hovers over it

54 Views Asked by At

I have a circle that follows my cursor. I was wondering if I can add an effect so when the circle hovers over the text it turns only the text under the circle to black so you are able to see the text even when the white circle is on top of it.

I tried to make the text black when I hover over it using CSS but it wont work when the words are too big because the text out of the circle contrast with the background.

I want only the text under the circle to turn black and the rest of it outside of the circle to remain white. like if the circle is over the "hel" in "hello world" I want the "hel" to be black and the "lo world" to be white

document.addEventListener('DOMContentLoaded', () => {
  const interBubble = document.getElementById('circle');
  let curX = 0;
  let curY = 0;
  let tgX = 0;
  let tgY = 0;

  function move() {
    curX += (tgX - curX) / 10;
    curY += (tgY - curY) / 10;
    interBubble.style.transform = `translate(${Math.round(curX)}px, ${Math.round(curY)}px)`;
    requestAnimationFrame(() => {
      move();
    });
  }

  window.addEventListener('mousemove', (e) => {
    tgX = e.clientX;
    tgY = e.clientY;

    if (e.target.tagName === 'P' ||
      e.target.tagName === 'A' ||
      e.target.tagName === 'BUTTON' ||
      e.target.parentNode.tagName === 'BUTTON') {
      interBubble.classList.add('big');
    } else {
      interBubble.classList.remove('big');
    }
  });

  move();
});
Body {
  background-image: linear-gradient(red, yellow);
  overflow: hidden;
}

div {
  position: relative;
  width: 100%;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 20%;
  z-index: 2;
}

p {
  color: white;
  font-size: 30px;
}

p:hover {
  color: black;
}

:root {
  --trans-bounce: cubic-bezier(.4,2.2,.6,1.0);
  --trans-time: .4s;
}

.mouseFollowCircle {
  width: 30px;
  height: 30px;
  border-radius: 999px;
  position: absolute;
  z-index: 1;
  top: -15px;
  left: -15px;
  box-shadow: 0 0 10px white;
  background-color: white;
  pointer-events: none;
  transition: width var(--trans-time) var(--trans-bounce), height var(--trans-time) var(--trans-bounce), top var(--trans-time) var(--trans-bounce), left var(--trans-time) var(--trans-bounce), background-color var(--trans-time) var(--trans-bounce);
}

.mouseFollowCircle.big {
  width: 70px;
  height: 70px;
  border-radius: 999px;
  position: absolute;
  z-index: 1;
  top: -35px;
  left: -35px;
  box-shadow: 0 0 10px white;
  background-color: white;
  pointer-events: none;
  transition: width var(--trans-time) var(--trans-bounce), height var(--trans-time) var(--trans-bounce), top var(--trans-time) var(--trans-bounce), left var(--trans-time) var(--trans-bounce), background-color var(--trans-time) var(--trans-bounce);
}
<div><p>X</p><p>Hello World</p></div>
<section class="mouseFollowCircle" id="circle"></section>

1

There are 1 best solutions below

1
A Haworth On

mix-blend-mode: difference will change white on white to black and white on black will be white.

However, the two elements have to be at the same level so z-index and transforms are removed in this snippet. The transform property is replaced by margin-top and -left (thanks to this Does CSS mix-blend-mode work with transform?)

document.addEventListener('DOMContentLoaded', () => {
  const interBubble = document.getElementById('circle');
  let curX = 0;
  let curY = 0;
  let tgX = 0;
  let tgY = 0;

  function move() {
    curX += (tgX - curX) / 10;
    curY += (tgY - curY) / 10;
    //interBubble.style.transform = `translate(${Math.round(curX)}px, ${Math.round(curY)}px)`;
    interBubble.style.marginTop = `${Math.round(curY)}px`;
    interBubble.style.marginLeft = `${Math.round(curX)}px`;
    requestAnimationFrame(() => {
      move();
    });
  }

  window.addEventListener('mousemove', (e) => {
    tgX = e.clientX;
    tgY = e.clientY;

    if (e.target.tagName === 'P' ||
      e.target.tagName === 'A' ||
      e.target.tagName === 'BUTTON' ||
      e.target.parentNode.tagName === 'BUTTON') {
      interBubble.classList.add('big');
    } else {
      interBubble.classList.remove('big');
    }
  });

  move();
});
body {
  background-color: black;
  overflow: hidden;
}

div {
  position: relative;
  width: 100%;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
}

p {
  color: white;
  font-size: 30px;
}

:root {
  --trans-bounce: cubic-bezier(.4, 2.2, .6, 1.0);
  --trans-time: .4s;
}

.mouseFollowCircle {
  width: 30px;
  height: 30px;
  border-radius: 999px;
  position: absolute;
  top: -15px;
  left: -15px;
  box-shadow: 0 0 10px white;
  background-color: white;
  pointer-events: none;
  /*backdrop-filter: blur(2px);*/
  transition: width var(--trans-time) var(--trans-bounce), height var(--trans-time) var(--trans-bounce), top var(--trans-time) var(--trans-bounce), left var(--trans-time) var(--trans-bounce), background-color var(--trans-time) var(--trans-bounce);
}

.mouseFollowCircle.big {
  width: 70px;
  height: 70px;
  border-radius: 999px;
  position: absolute;
  top: -35px;
  left: -35px;
  box-shadow: 0 0 10px white;
  background-color: white;
  pointer-events: none;
  /*backdrop-filter: blur(2px);*/
  transition: width var(--trans-time) var(--trans-bounce), height var(--trans-time) var(--trans-bounce), top var(--trans-time) var(--trans-bounce), left var(--trans-time) var(--trans-bounce), background-color var(--trans-time) var(--trans-bounce);
  mix-blend-mode: difference;
}
<div>
  <p>Hello World</p>
</div>
<section class="mouseFollowCircle" id="circle"></section>

Note I commented out the filter as I wasn't sure you wanted blurry text.

UPDate: the original question had a black background for which this answer works. The question has changed to have a gradient background for which this answer doesn’t work as the white circle picks up color.