Mouse-movement image-layer effect

Viewed 655

I'd like to create the following effect:

enter image description here

There are two full-screen images on top of each other, but only one is visible. When you move the mouse, the second one is revealed within a circle where the mouse is.

I know how to create the circle that follows the mouse using JS. As for the image overlays, I'm stumped. I fiddled with pseudo-elements, with clip-path and opacity, with radial-gradient, with multiple backgrounds -- to no avail.

Radial-gradient would actually be ideal here, but as far as I know it only accepts colors, not images.

Perhaps a third overlay layer? Any ideas? (If there's already a CodePen that does this and that I've missed, please link to it).

Thanks y'all!

2 Answers

short answer: (and all the code below is commented)

I used background property in a <div> instead of using a <img> tag...
the trick is when you use background-attachment: fixed;

enter image description here

in this example, I used 2 backgrounds from Windows 11 OS and you can see really the trick! one dark and one light...

detailed explanation:

  1. so the a <div> and assigned a background-image to the URL of the light-image

the problem here is: when the div is moving with JS, also the image move...
but I find a solution

  1. make the image fixed, which make the image be always in the same position, also if the image is moving
#circle {
    background-attachment: fixed;
 
    /* your code */
}

the problem here is: the image isn't responsive,
but I find solution:

  1. make the image size to cover so it will auto-adjust the size and height of the image automatically to the viewport of the device.
#circle {
    background-size: cover;
 
    /* your code */
}
  1. for moving is simple... because we can use Javascript, with an eventListener of mousemove
document.addEventListener('mousemove', (e) => {
   /* your code */
});

now solved all the problems, however, I will put some documentations below

documentation that can help you:

here the code

the code can seem to be long,
but not really because I added a lot of comments so everyone can understand

let circle = document.getElementById('circle');

// on mouse move move the circle
document.addEventListener('mousemove', (e) => {
  // make the image move relative to the mouse (make sure that in css you applied position: relative; to the div)
  circle.style.left = e.pageX - 100 + 'px'; // 100 is half height of circle, so the cursor is in the middle
  circle.style.top = e.pageY - 100 + 'px';
});
body {
  margin: 0;
  overflow: hidden;
}

#bg-image {
  position: fixed;
  height: 100vh;
  width: 100vw;
  /*make the background responsive*/
  object-fit: cover;
  /* under the circle div*/
  z-index: -1;
}

#circle {
  height: 200px;
  width: 200px;
  /* make the the div circle */
  border-radius: 50%;
  /* important using relative for using top and left in javascript */
  position: relative;
  /* change the url with the link of image you want */
  background-image: url(https://laaouatni.github.io/w11CSS/images/0light.jpg);
  /*center the background */
  background-position: center;
  background-repeat: no-repeat;
  /* TRICK: making the background responsive*/
  background-size: cover;
  /* TRICK: the MAGIC is HERE make the image fixed, so is not moving  */
  background-attachment: fixed;
}
<!-- background image -->
<img id="bg-image" src=" https://laaouatni.github.io/w11CSS/images/1dark.jpg" alt=" ">

<!-- the circle, and moved with javascript -->
<div id="circle"></div>

please see in full mode,
for better results... you can change the values to something like vh or vw for making this more responsive.

I hope this will help you.

You can use a radial gradient as a mask in CSS.

This snippet has an 'underneath' element which is in fact above the background on the body element.

The mask-image cuts out all but the part of its background which is underneath the non-transparent part.

Obviously you will want to alter the dimensions to fit your use case.

Also, the mousemove does literally that (not looking to see whether mousedown for example) just to give a simple demonstration.

Note that some browsers still require a -webkit- prefix for masks.

<!doctype html>
<html>

<head>
  <style>
    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
    }
    
    body {
      margin: 0;
      background-image: url(https://picsum.photos/id/1016/1024/768);
      width: 100vw;
      height: 100vh;
      background-size: cover;
      background-position: center;
    }
    
    .underneath {
      position: absolute;
      width: 100%;
      height: 100%;
      --x: 50vw;
      --y: 50vh;
      background-image: url(https://picsum.photos/id/1015/1024/768);
      background-size: cover;
      background-position: center;
      -webkit-mask-image: radial-gradient(black 0 15vmin, transparent 15vmin 30vmin);
      -webkit-mask-size: 30vmin 30vmin;
      -webkit-mask-repeat: no-repeat;
      -webkit-mask-position: calc(var(--x) - 15vmin) calc(var(--y) - 15vmin);
      mask-image: radial-gradient(black 0 15vmin, transparent 15vmin 30vmin);
      mask-size: 30vmin 30vmin;
      mask-repeat: no-repeat;
      mask-position: calc(var(--x) - 15vmin) calc(var(--y) - 15vmin);
    }
  </style>
</head>

<body>
  <div class="underneath"></div>
  <script>
    const underneath = document.querySelector('.underneath');
    document.body.addEventListener('mousemove', function() {
      underneath.style.setProperty('--x', event.clientX + 'px');
      underneath.style.setProperty('--y', event.clientY + 'px');

    });
  </script>
</body>

Related