Style HTML sections with CSS

by Jordan Sayner

HTML

<div class="image-hover-sliced" data-slices="7" data-move="20">
  <img src="https://picsum.photos/id/1015/800/500" alt="Hover Slice" />
</div>

<div class="image-hover-sliced" data-slices="3" data-move="5">
  <img src="https://picsum.photos/id/1016/800/500" alt="Hover Slice" />
</div>



<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/ScrollTrigger.min.js"></script>

CSS

.image-hover-sliced {
  position: relative;
  width: 100%;
  max-width: 800px;
  margin: 2rem auto;
  aspect-ratio: 16 / 10;
  cursor: pointer;
}

.image-hover-sliced img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  position: relative;
  z-index: 1;
}

.canvas-slice {
  position: absolute;
  top: 0;
  left: 0;
  pointer-events: none;
  z-index: 2;
  transform: translateY(0);
  height: 100%;
}

JavaScript

document.querySelectorAll(".image-hover-sliced").forEach((container) => {
    const img = container.querySelector("img");
    const sliceCount = parseInt(container.dataset.slices || 5);
    const moveAmount = parseInt(container.dataset.move || 40);

    function initSlices() {
      container.querySelectorAll(".canvas-slice").forEach(el => el.remove());

      const rect = img.getBoundingClientRect();
      const width = rect.width;
      const height = rect.height;
      const sliceWidth = width / sliceCount;

      img.style.visibility = "hidden";

      for (let i = 0; i < sliceCount; i++) {
        const canvas = document.createElement("canvas");
        canvas.width = sliceWidth;
        canvas.height = height;
        canvas.classList.add("canvas-slice");
        canvas.style.left = `${i * sliceWidth}px`;
        canvas.style.width = `${sliceWidth}px`;

        const ctx = canvas.getContext("2d");
        ctx.drawImage(
          img,
          i * sliceWidth,
          0,
          sliceWidth,
          height,
          0,
          0,
          sliceWidth,
          height
        );

        container.appendChild(canvas);
      }
    }

    img.addEventListener("load", initSlices);
    if (img.complete) initSlices();

    window.addEventListener("resize", () => {
      initSlices();
    });

    container.addEventListener("mouseenter", () => {
      container.querySelectorAll(".canvas-slice").forEach((slice, i) => {
        gsap.to(slice, {
          y: i % 2 === 0 ? -moveAmount : moveAmount,
          duration: 0.6,
          ease: "power2.out",
          delay: i * 0.05,
        });
      });
    });

    container.addEventListener("mouseleave", () => {
      container.querySelectorAll(".canvas-slice").forEach((slice, i) => {
        gsap.to(slice, {
          y: 0,
          duration: 0.6,
          ease: "power2.inOut",
          delay: (sliceCount - i - 1) * 0.03,
        });
     ...