JueK3y
JueK3y

Reputation: 317

When hovering over image, only display the part where the cursor is over it

Info in advance:
You can see the current code or the live demo (without the working hover image) at https://juek3y.com.

The idea:
I want, when I hover with my cursor (a black circle) over the image, to get displayed only exactly this part of the image (and not the whole image), over which the cursor is hovering.

The problem:
Where do I start? I have seen a few CodePens that work with Z-Index. However, I have not been successful with them so far. Also a sample website that has something like this would be enough for me.


For a better understanding I have two more example pictures.

enter image description here

In the first image, the smaller circle / cursor (black / turquoise circle) hovers over the round text, but it does not yet intersect the image. Therefore this is hidden.


enter image description here
In the second image, the cursor is already hovering a bit above the image, this part of the image is also shown.

Upvotes: 0

Views: 3122

Answers (2)

A Haworth
A Haworth

Reputation: 36574

You can use CSS mask to 'cut a hole' in an element.

You can put the image that is to be (partially) revealed as background to a div, then overlay that with an after pseudo-element which has as background the image you want at the front, masked with a radial gradient which gets positioned with the mouse position.

This snippet overlays a colored image with a grayscale one as an example:

window.onload = function() {
  const div = document.querySelector('.hole');
  let isIn = false;
  div.addEventListener('mouseover', function() {
    isIn = true;
  });
  div.addEventListener('mouseout', function() {
    isIn = false;
  });
  div.addEventListener('mousemove', function() {
    if (isIn) {
      div.style.setProperty('--x', event.clientX + 'px');
      div.style.setProperty('--y', event.clientY + 'px');
    }
  });
}
.hole {
  background-image: url(https://picsum.photos/id/1016/1024/768);
  background-size: cover;
  --x: 200px;
  --y: 150px;
  width: 100vw;
  height: 100vh;
  position: relative;
}

.hole::after {
  content: '';
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
  background-image: url(https://picsum.photos/id/1016/1024/768?grayscale);
  background-size: cover;
  z-index: 1;
  -webkit-mask-repeat: no-repeat no-repeat;
  mask-repeat: no-repeat no-repeat;
  -webkit-mask-image: radial-gradient(200px at var(--x) var(--y), transparent 95%, black 100%);
  mask-image: radial-gradient(200px at var(--x) var(--y), transparent 95%, black 100%);
}
<div class="hole"></div>

Upvotes: 2

pilchard
pilchard

Reputation: 12918

You can use the CSS clip-path property in conjunction with a mousemove listener to show the front image under the mouse, and clearing the clip-path on mouseleave.

Rough working snippet using a CSS invert filter on the overlay image. You could easily add event delegation for handling multiple overlays etc.

const overlay = document.querySelector('.overlay');

overlay.addEventListener('mousemove', e => {
  const {height, width} = e.currentTarget.getBoundingClientRect();
  
  const x = e.offsetX;
  const y = e.offsetY;
  
  const xPercent = Math.round((x/width)*100);
  const yPercent = Math.round((y/height)*100);

  e.currentTarget.style.clipPath = `circle(18% at ${xPercent}% ${yPercent}%)`;
});

overlay.addEventListener('mouseleave', e => {
  e.currentTarget.style.clipPath = null; 
});
.container {
  position: relative;
}

.container img {
  width: 200px;
  height: 200px;
  position: absolute;
  top: 0;
  left: 0;
}

.overlay {
  filter: invert(var(--value, 100%));
  clip-path: circle(0 at 49% 45%);
}
<div class='container'>
  <img src='https://source.unsplash.com/random' />    
  <img src='https://source.unsplash.com/random' class='overlay'/>
</div>

Upvotes: 1

Related Questions