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,
});
...