javascripthtmlcssclip-pathmousehover

Clip-path doesn't work corectly on web browswers (Mozilla, Edge, Chrome) but works perfectly on preview Visual Code Studio HTML, CSS, JS


I want to create a fancy hero section for my company. I'm using clip-path and some JS to achieve a paint spray effect on mouse move, and it works perfectly in the VCS preview. However, when I try it in web browsers like Chrome, Mozilla, or Edge, it doesn't work. It seems like the cursor's position can't be found properly. The clip-path shows in random places and for a few seconds or doesn't show at all. What's more? if its i left corner without margin: auto; it's work great but it had to be center.

Page where i want to use it: https://hoffmannteile.de/ Here's the code I'm using (it has to be in one file because it's a closed CMS):

document.addEventListener('DOMContentLoaded', function () {
    const cars = document.querySelectorAll('.car');
    cars.forEach(car => {
        car.addEventListener('mousemove', function (e) {
            const x = e.pageX - car.offsetLeft;
            const y = e.pageY - car.offsetTop;

            car.style.setProperty('--x', x + 'px');
            car.style.setProperty('--y', y + 'px');
        });
    });
});
.paint {
  max-height: 338px;
  max-width: 600px;
  margin: auto;
}

.malowanie {
  display: flex;
  justify-content: center;
  align-items: center;
  position: relative;
}

.cont {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
}

.cont .car {
  position: relative;
  height: 338px;
  width: 600px;
  background-image: url('https://composer.idosell.com/gfx/30226/SprinterWhite.png');
  background-size: cover;
  display: flex;
  justify-content: center;
  align-items: center;
}

.cont .car::before {
  content: '';
  position: absolute;
  z-index: 1;
}

.cont .car::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--img);
  background-size: cover;
  clip-path: circle(0px at 0 0);
  z-index: 2;
  transition: clip-path 0.15s;
}

.cont .car:hover::after {
  clip-path: circle(100px at var(--x) var(--y));
  cursor: url('https://composer.idosell.com/gfx/30226/paint-spray-icon.svg'), auto;
}
<section class="paint">
    <div class="malowanie">
        <div class="cont">
            <div class="car" style="--img: url('https://composer.idosell.com/gfx/30226/SprinterRedHof.png')"></div>
        </div>
    </div>
</section>


Solution

  • Use mouse coordinate relative to the clientX/Y and use Element.getBoundingClientRect() left and top values

    document.addEventListener('DOMContentLoaded', function() {
      const cars = document.querySelectorAll('.car');
      cars.forEach(car => {
        car.addEventListener('mousemove', function(e) {
          const bcr = car.getBoundingClientRect();
          const x = e.clientX - bcr.left;
          const y = e.clientY - bcr.top;
          car.style.setProperty('--x', x + 'px');
          car.style.setProperty('--y', y + 'px');
        });
      });
    });
    body {
      min-height: 300px; /* just to test scroll */
    }
    
    .paint {
      max-height: 338px;
      max-width: 600px;
      margin: auto;
    }
    
    .malowanie {
      display: flex;
      justify-content: center;
      align-items: center;
      position: relative;
    }
    
    .cont {
      position: relative;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    
    .cont .car {
      position: relative;
      height: 338px;
      width: 600px;
      background-image: url('https://composer.idosell.com/gfx/30226/SprinterWhite.png');
      background-size: cover;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    
    .cont .car::before {
      content: '';
      position: absolute;
      z-index: 1;
    }
    
    .cont .car::after {
      content: '';
      position: absolute;
      inset: 0;
      background: var(--img);
      background-size: cover;
      clip-path: circle(0px at 0 0);
      z-index: 2;
      transition: clip-path 0.15s;
    }
    
    .cont .car:hover::after {
      clip-path: circle(100px at var(--x) var(--y));
      cursor: url('https://composer.idosell.com/gfx/30226/paint-spray-icon.svg'), auto;
    }
    <h1>Some title to test offset</h1>
    <section class="paint">
      <div class="malowanie">
        <div class="cont">
          <div class="car" style="--img: url('https://composer.idosell.com/gfx/30226/SprinterRedHof.png')"></div>
        </div>
      </div>
    </section>